
通过实现org.springframework.security.core.userdetails.UserDetailsService接口,实现其用户(认证主体)查询方法即可,参见第四步。
设计用户数据库表,如:

CREATE TABLE `user_info` (
`id` int NOT NULL AUTO_INCREMENT,
`username` varchar(50) COLLATE utf8mb4_general_ci NOT NULL,
`password` varchar(100) COLLATE utf8mb4_general_ci NOT NULL,
`remark` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `username` (`username`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci为框架操作数据库添加依赖,以mybatis-plus为例
<!-- 引入mybatis-plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.0</version>
</dependency>
<!-- 引入数据库驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.16</version>
</dependency>另外为了简化代码,可以根据需要引入lombok等工具
<!-- 引入lombok 简化代码 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
</dependency>这一步需要创建mybatis操作数据库所需的实体和操作接口,示例代码如下:
import lombok.Data;
/**
* 数据库映射实体
* 映射数据表: user_info
*/
@Data
public class UserInfo {
private Integer id;
private String username;
private String password;
private String remark;
}import cn.com.demo.entity.UserInfo;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.springframework.stereotype.Repository;
@Repository
public interface UserInfoDao extends BaseMapper<UserInfo> {
}import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
// 配置MybatisMapper扫描路径
@MapperScan("cn.com.demo.dao.**")
@SpringBootApplication(scanBasePackages = "cn.com.demo.**")
public class SecurityApplication {
public static void main(String[] args) {
SpringApplication.run(SecurityApplication.class,args);
}
}这一步较为关键,为security提供认证主体查询接口,示例代码如下: 代码参考示例如下;
import cn.com.demo.dao.UserInfoDao;
import cn.com.demo.entity.UserInfo;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class UserDetailService implements UserDetailsService {
@Autowired
private UserInfoDao userInfoDao;
@Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
@Override
public UserDetails loadUserByUsername(String usename) throws UsernameNotFoundException {
// 查询数据库
QueryWrapper<UserInfo> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("username",usename);
UserInfo userInfo = userInfoDao.selectOne(queryWrapper);
if (userInfo ==null) {
throw new UsernameNotFoundException("用户不存在");
}
List<GrantedAuthority> auths = AuthorityUtils.commaSeparatedStringToAuthorityList("role");
// 返回认证主体UserDetails
return new User(userInfo.getUsername(),bCryptPasswordEncoder.encode(userInfo.getPassword()),auths);
}
}这里还需要注意下,密码加密的工具类可以自行设置,也可以适用内置的BCryptPasswordEncoder 完成,本demo中将BCryptPasswordEncoder 声明成了spring的一个bean进行管理,当然这里也可以直接使用其new方法完成创建。
此步骤为提供数据库连接信息,用于完成数据库操作配置;修改配置文件,如:application.yml
spring:
datasource:
url: jdbc:mysql://localhost:3306/springsecurity?serverTimezone=Asia/Shanghai
username: springsecurity
password: springsecurity@1
driver-class-name: com.mysql.cj.jdbc.Driver实现UserDetailsService接口的目的是为security提供认证主体相关的信息,这一步也可以通过配置进行覆盖,示例代码如下:
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
// 声明 认证主体服务对象及加密方式
authenticationManagerBuilder.userDetailsService(userDetailService).passwordEncoder(bCryptPasswordEncoder);
}
}编写测试接口,访问即可完成测试(记得数据库中添加用户及密码信息)
重写密码加密demo示例
import cn.com.demo.util.MD5;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
@Component
public class MyPasswordEncoder implements PasswordEncoder {
/**
* 加密密码
* @param rawPassword 原始密码
* @return
*/
@Override
public String encode(CharSequence rawPassword) {
return MD5.encrypt(rawPassword.toString());
}
/**
* 【重点】密码比对
* @param rawPassword 原始密码(页面提交过来的)
* @param encodedPassword 已存储的密码(数据库等)
* @return
*/
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
return encodedPassword.equalsIgnoreCase(MD5.encrypt(rawPassword.toString()));
}
}配置自定义密码加密
import cn.com.demo.service.UserDetailService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
UserDetailService userDetailService;
@Autowired
private MyPasswordEncoder myPasswordEncoder;
@Override
protected void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
//设置自定义的密码加密方式
authenticationManagerBuilder.userDetailsService(userDetailService).passwordEncoder(myPasswordEncoder);
}
}使用自定义的PasswordEncoder 时要注意在四步的时候设置认证主体的密码应该对应匹配,即如果数据查询出来的已经是加密之后的密码,第四步就不应该再进行加密。