- 注重版权,转载请注明原作者和原文链接
作者:码农BookSea
原文链接: https://blog.csdn.net/bookssea/article/details/109262109
先看后赞,养成习惯。
点赞收藏,人生辉煌。
基于数据库的身份认证
一、创建项目
创建一个 SpringBoot 模块项目,选择相关依赖:
先搭建项目正常访问,在pom.xml中,先把Spring Security依赖注释
<!--<dependency>--> <!--<groupId>org.springframework.boot</groupId>--> <!--<artifactId>spring-boot-starter-security</artifactId>--> <!--</dependency>-->
在MySQL数据库中创建一张用户表,id主键自增,并添加两个用户如下:
代码:
创建entity实体
@Data public class UserInfo { private int id; private String username; private String password; private String role; }
创建mapper接口
@Mapper @Repository public interface UserInfoMapper { @Select("select * from user where username = #{username}") UserInfo getUserInfoByUsername(String username); } 创建UserInfoService.java文件 @Service public class UserInfoService { @Autowired private UserInfoMapper userInfoMapper; public UserInfo getUserInfo(String username){ return userInfoMapper.getUserInfoByUsername(username); } }
创建HelloController.java文件
@RestController public class HelloController { @Autowired private UserInfoService userInfoService; @GetMapping("/get-user") public UserInfo getUser(@RequestParam String username){ return userInfoService.getUserInfoByUsername(username); } }
配置文件application.yml中设置MySQL连接配置和MyBatis配置
spring: datasource: url: jdbc:mysql://localhost:3306/security?useSSL=FALSE&serverTimezone=UTC username: root password: root driver-class-name: com.mysql.cj.jdbc.Driver logging: level: com.example.bdatabaserole.mapper: debug # 打印sql语句
以上配置好后,启动项目,访问localhost:8080/get-user?username=user,正常获取到用户信息:
下面开始使用Spring Security安全验证:
二、Spring Security基于数据库认证
把pom.xml中的Spring Security依赖注释去掉:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>
要从数据库读取用户信息进行身份认证,需要新建类实现UserDetailService接口重写loadUserByUsername方法:
@Component public class CustomUserDetailsService implements UserDetailsService { @Autowired private UserInfoService userInfoService; /** * 需新建配置类注册一个指定的加密方式Bean,或在下一步Security配置类中注册指定 */ @Autowired private PasswordEncoder passwordEncoder; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { // 通过用户名从数据库获取用户信息 UserInfo userInfo = userInfoService.getUserInfo(username); if (userInfo == null) { throw new UsernameNotFoundException("用户不存在"); } // 得到用户角色 String role = userInfo.getRole(); // 角色集合 List<GrantedAuthority> authorities = new ArrayList<>(); // 角色必须以`ROLE_`开头,数据库中没有,则在这里加 authorities.add(new SimpleGrantedAuthority("ROLE_" + role)); return new User( userInfo.getUsername(), // 因为数据库是明文,所以这里需加密密码 passwordEncoder.encode(userInfo.getPassword()), authorities ); } }
创建Security的配置类WebSecurityConfig继承WebSecurityConfigurerAdapter,并重写configure(auth)方法:
@EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private MyUserDatailService userDatailService; /** * 指定加密方式 */ @Bean public PasswordEncoder passwordEncoder(){ // 使用BCrypt加密密码 return new BCryptPasswordEncoder(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth // 从数据库读取的用户进行身份认证 .userDetailsService(userDatailService) .passwordEncoder(passwordEncoder()); } }
上面设置完后,重新启动,在登录页面就可以输入数据库中的用户名/密码了。
三、角色访问控制
上面设置后,可以使用数据库中的用户名/密码登录,还获取到了用户的角色。通过用户的角色,可以限制用户的请求访问:
开启方法的访问权限,需要在WebSecurityConfig添加
@EnableGlobalMethodSecurity注解 @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) // 开启方法级安全验证 public class WebSecurityConfig extends WebSecurityConfigurerAdapter { ... }
修改UserController.java类,增加方法的访问权限
@RestController public class HelloController { @Autowired private UserInfoService userInfoService; @GetMapping("/get-user") public UserInfo getUser(@RequestParam String username){ return userInfoService.getUserInfo(username); } @PreAuthorize("hasAnyRole('user')") // 只能user角色才能访问该方法 @GetMapping("/user") public String user(){ return "user角色访问"; } @PreAuthorize("hasAnyRole('admin')") // 只能admin角色才能访问该方法 @GetMapping("/admin") public String admin(){ return "admin角色访问"; } }
配置完后,重新启动程序,在登录页面中:
输入user/user123登录,该用户角色为user,可以访问localhost:8080/user,不能访问localhost:8080/admin;
再重启程序输入admin/admin123登录,角色为admin,能访问localhost:8080/admin,不能访问localhost:8080/user。
四、密码加密保存
前面的用户密码都是手动添加的,所以数据库中是明文显示,在实际开发中,都是需要加密保存的。
下面模拟简单注册用户,加密保存密码:
UserInfoMapper.java类中添加插入用户
@Mapper @Repository public interface UserMapper { ... // 插入用户 @Insert("insert into user(username, password, role) value(#{username}, #{password}, #{role})") int insertUserInfo(UserInfo userInfo); }
UserInfoService.java类中添加插入方法,注意要加密密码
@Service public class UserInfoService { ... @Autowired private PasswordEncoder passwordEncoder; ... public int insertUser(UserInfo userInfo){ // 加密密码 userInfo.setPassword(passwordEncoder.encode(userInfo.getPassword())); return userInfoMapper.insertUserInfo(userInfo); }
}
修改HelloController.java,增加添加用户接口
@RestController public class HelloController { ... @PostMapping("/add-user") public int addUser(@RequestBody UserInfo userInfo){ return userInfoService.insertUser(userInfo); } }
配置完后,启动服务,使用Postman发送POST请求来添加用户:
点击Send按钮后,添加失败,不会返回成功1,看到红框的状态码显示401 Unauthorized,说明无权限,需要登录,但注册用户是不用登录的,所以就需要注册用户的请求无需身份验证:
修改WebSecurityConfig配置类,重写configure(HttpSecurity http)方法,配置允许注册用户的请求访问:
@EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) public class WebSecurityConfig extends WebSecurityConfigurerAdapter { ... @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers(HttpMethod.POST, "/add-user").permitAll() // 允许post请求/add-user,而无需认证 .anyRequest().authenticated() // 所有请求都需要验证 .and() .formLogin() // 使用默认的登录页面 .and() .csrf().disable();// post请求要关闭csrf验证,不然访问报错;实际开发中开启,需要前端配合传递其他参数 } }
配置允许POST请求/add-user访问后,再在Postman发送请求就可以成功了:
图片
查看数据库数据,添加的用户密码已加密:
使用加密密码登录,需要修改CustomUserDetailsService类,之前从数据库拿到明文密码后需要加密,现在数据库里面的密码已经加密了,就不用加密了:
@Component public class MyUserDatailService implements UserDetailsService { //@Autowired //private PasswordEncoder passwordEncoder; ... @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { ... return new User( user.getUsername(), // 数据库密码已加密,不用再加密 user.getPassword(), authorities ); } }
浏览器访问localhost:8080/user,输入zeng/zeng123登录即可。