当前位置:   article > 正文

SpringSecurity入门学习(2)整合MyBatis-Plus将用户存入数据库_mybatisplus修改数据之后怎么放进数据库

mybatisplus修改数据之后怎么放进数据库

前面学习了Security简单的认证和授权,而我们使用的登录用户是在内存中或者配置文件中定义的,而实际项目中我们都是在数据库中定义用户,接下来我们开始学习如何将用户数据保存到数据库。

Security整合Mybatis-Plus将用户存入数据库

SpringSecurity支持多种不同的数据源,这些不同的数据源最终都将被封装成UserDetailsService的实例,可以自己封装,也可以使用系统默认提供的UserDetailsService实例,例如前面介绍的InMemoryUserDetailsManager;在UserDetailsService的实现类中,除了InMemoryUserDetailsManager之外,还有一个JdbcUserDetailsManager,使用JdbcUserDetailsManager可以让我们通过 JDBC 的方式将数据库和SpringSecurity连接起来,但是JdbcUserDetailsManager使用起来不是很方便,感兴趣的小伙伴可以自己去了解一下。

这里我们自己封装UserDetailsService实例,为了操作方便我们使用Mybatis-Plus来完成数据库操作,数据库使用H2Database,大家可以使用MySql都是一样的。

1.创建项目

可以参考我这篇文章SpringBoot整合MyBatis-Plus+Druid,来整合SpringBoot、MyBatis-Plus、H2Database

创建新的SpringBoot项目,引入以下相关依赖

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-web</artifactId>
  4. </dependency>
  5. <dependency>
  6. <groupId>com.h2database</groupId>
  7. <artifactId>h2</artifactId>
  8. <scope>runtime</scope>
  9. </dependency>
  10. <dependency>
  11. <groupId>org.projectlombok</groupId>
  12. <artifactId>lombok</artifactId>
  13. <optional>true</optional>
  14. </dependency>
  15. <dependency>
  16. <groupId>com.alibaba</groupId>
  17. <artifactId>fastjson</artifactId>
  18. <version>1.2.70</version>
  19. </dependency>
  20. <dependency>
  21. <groupId>com.baomidou</groupId>
  22. <artifactId>mybatis-plus-boot-starter</artifactId>
  23. <version>3.3.2</version>
  24. </dependency>
  25. <dependency>
  26. <groupId>com.baomidou</groupId>
  27. <artifactId>mybatis-plus-generator</artifactId>
  28. <version>3.3.2</version>
  29. </dependency>
  30. <dependency>
  31. <groupId>org.apache.velocity</groupId>
  32. <artifactId>velocity-engine-core</artifactId>
  33. <version>2.2</version>
  34. </dependency>
  35. <dependency>
  36. <groupId>cn.hutool</groupId>
  37. <artifactId>hutool-all</artifactId>
  38. <version>5.3.5</version>
  39. </dependency>

配置application.properties配置文件

  1. # DataSource Config
  2. spring.datasource.driver-class-name=org.h2.Driver
  3. # 配置本地数据库地址,按需修改
  4. # DATABASE_TO_LOWER=TRUE;-要求数据库名称小写 CASE_INSENSITIVE_IDENTIFIERS=TRUE;-要求对字段的大小写不敏感
  5. spring.datasource.url=jdbc:h2:D:/symon/project/db/security3;CASE_INSENSITIVE_IDENTIFIERS=TRUE;
  6. spring.datasource.username=symon
  7. spring.datasource.password=123456
  8. # 指定Schema (DDL)脚本
  9. spring.datasource.schema=classpath:db/schema.sql
  10. # 指定Data (DML)脚本
  11. spring.datasource.data=classpath:db/data.sql
  12. # 指定schema要使用的Platform
  13. spring.datasource.platform=h2
  14. # 是否启用h2控制台
  15. spring.h2.console.enabled=true
  16. # 配置h2控制台访问地址,http://localhost:8080/h2
  17. spring.h2.console.path=/h2
  18. # MyBatis-Plus配置
  19. mybatis-plus.mapper-locations=classpath:mapper/*.xml

在resources下创建db文件夹,并创建schema.sql和data.sql脚本,分别用来填写建表语句和初始化几条数据

  1. #schema.sql
  2. CREATE TABLE IF NOT EXISTS sys_user
  3. (
  4. ID bigint(20) NOT NULL AUTO_INCREMENT COMMENT '用户ID',
  5. USERNAME varchar(50) NOT NULL COMMENT '用户名',
  6. PASSWORD varchar(128) NOT NULL COMMENT '密码',
  7. STATUS INT(2) NOT NULL COMMENT '状态 0锁定 1有效',
  8. CREATE_TIME datetime(0) NOT NULL COMMENT '创建时间',
  9. MODIFY_TIME datetime(0) NULL DEFAULT NULL COMMENT '修改时间',
  10. PRIMARY KEY (ID)
  11. );
  12. CREATE TABLE IF NOT EXISTS sys_role (
  13. ID bigint(20) NOT NULL AUTO_INCREMENT COMMENT '角色ID',
  14. ROLE_CODE varchar(100) NOT NULL COMMENT '角色标识',
  15. ROLE_NAME varchar(100) NOT NULL COMMENT '角色名称',
  16. CREATE_TIME datetime(0) NOT NULL COMMENT '创建时间',
  17. MODIFY_TIME datetime(0) NULL DEFAULT NULL COMMENT '修改时间',
  18. PRIMARY KEY (ID)
  19. );
  20. CREATE TABLE IF NOT EXISTS sys_user_role (
  21. ID bigint(20) NOT NULL AUTO_INCREMENT COMMENT '用户角色关联ID',
  22. USER_ID bigint(20) NOT NULL COMMENT '用户ID',
  23. ROLE_ID bigint(20) NOT NULL COMMENT '角色ID',
  24. PRIMARY KEY (ID)
  25. ) ;
  26. #data.sql
  27. DELETE FROM sys_user;
  28. INSERT INTO sys_user VALUES (1, 'symon', '$2a$10$Dm9tmMfvISVWTmrCM9WwUeSwgdwYSgU48zkqbhEcuDgl40SJuDYsu', 1, '2020-07-21 20:39:22', '2020-07-21 20:44:42');
  29. INSERT INTO sys_user VALUES (2, 'test', '$2a$10$Dm9tmMfvISVWTmrCM9WwUeSwgdwYSgU48zkqbhEcuDgl40SJuDYsu', 1, '2020-07-21 20:39:22', '2020-07-21 20:44:42');
  30. INSERT INTO sys_user VALUES (3, 'test1', '$2a$10$Dm9tmMfvISVWTmrCM9WwUeSwgdwYSgU48zkqbhEcuDgl40SJuDYsu', 0, '2020-07-21 20:39:22', '2020-07-21 20:44:42');
  31. DELETE FROM sys_role;
  32. INSERT INTO sys_role VALUES (1, 'root', '管理员', '2020-07-21 20:39:22', '2020-07-21 20:44:42');
  33. INSERT INTO sys_role VALUES (2, 'user', '普通用户', '2020-07-21 20:39:22', '2020-07-21 20:44:42');
  34. DELETE FROM sys_user_role;
  35. INSERT INTO sys_user_role VALUES (1, 1, 1);
  36. INSERT INTO sys_user_role VALUES (2, 2, 2);
  37. INSERT INTO sys_user_role VALUES (3, 3, 2);

启动项目,之后访问登录h2控制台http://localhost:8080/h2,即可看到表已经创建成功

 

然后终止项目,使用MyBatis-Plus的代码生成器快速生成代码,可参考我这篇文章SpringBoot整合MyBatis-Plus+Druid

生成之后如下,我们可以看到实体类、mapper、service已经自动生成好了

 

2.配置

创建SysUserDetails类实现UserDetails接口中的方法

  1. public class SysUserDetails implements UserDetails {
  2. private SysUserDTO user;
  3. public SysUserDetails(SysUserDTO user) {
  4. this.user = user;
  5. }
  6. @Override
  7. public Collection<? extends GrantedAuthority> getAuthorities() {
  8. List<SimpleGrantedAuthority> authorities = new ArrayList<>();
  9. if (!CollectionUtils.isEmpty(user.getRoleList())) {
  10. for (SysRole sysRole : user.getRoleList()) {
  11. authorities.add(new SimpleGrantedAuthority("ROLE_" + sysRole.getRoleCode()));
  12. }
  13. }
  14. return authorities;
  15. }
  16. @Override
  17. public String getPassword() {return user.getPassword();}
  18. @Override
  19. public String getUsername() {return user.getUsername();}
  20. @Override
  21. public boolean isAccountNonExpired() {return true;}
  22. @Override
  23. public boolean isAccountNonLocked() {return true;}
  24. @Override
  25. public boolean isCredentialsNonExpired() {return true;}
  26. @Override
  27. public boolean isEnabled() {return Objects.equals(1, user.getStatus());}
  28. }

其中:

      SysUserDTO的内容如下:

  1. @Data
  2. public class SysUserDTO {
  3. private Long id;
  4. private String username;
  5. private String password;
  6. private Integer status;
  7. private LocalDateTime createTime;
  8. private LocalDateTime modifyTime;
  9. //用户关联角色
  10. private List<SysRole> roleList;
  11. }

      UserDetails中有四个字段,accountNonExpired、accountNonLocked、credentialsNonExpired、enabled 这四个字段分别用来描述用户的状态,表示账户是否没有过期、账户是否没有被锁定、密码是否没有过期、以及账户是否可用。

      getAuthorities 方法返回用户的角色信息,我们在这个方法中把自己的 Role 稍微转化一下即可。

创建SysUserDetailService实现UserDetailsService接口中的loadUserByUsername方法

  1. @Service
  2. public class SysUserDetailService implements UserDetailsService {
  3. @Autowired
  4. private SysUserService sysUserService;
  5. @Override
  6. public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
  7. SysUserDTO user = sysUserService.getUserDetail(username);
  8. if (user == null){
  9. throw new UsernameNotFoundException("用户名不存在!");
  10. }
  11. return new SysUserDetails(user);
  12. }
  13. }

其中:

      SysUserService是刚刚自动生成的类,其中有一个手动创建的方法,其内容如下

  1. @Service
  2. public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements SysUserService {
  3. @Autowired
  4. private SysUserMapper sysUserMapper;
  5. @Autowired
  6. private SysRoleMapper sysRoleMapper;
  7. @Override
  8. public SysUserDTO getUserDetail(String username) {
  9. Wrapper<SysUser> wrapper = new QueryWrapper<SysUser>().eq("username", username);
  10. SysUser sysUser = sysUserMapper.selectOne(wrapper);
  11. if (sysUser != null) {
  12. SysUserDTO user = BeanUtil.copyProperties(sysUser, SysUserDTO.class);
  13. List<SysRole> roleList = sysRoleMapper.selectByUserId(sysUser.getId());
  14. user.setRoleList(roleList);
  15. return user;
  16. } else {
  17. return null;
  18. }
  19. }
  20. }

      getUserDetail方法中的SysRoleMapper及其映射xml文件的的方法的内容如下,该方法查询用户关联的角色

  1. List<SysRole> selectByUserId(@Param("userId") Long userId);
  2. <select id="selectByUserId" resultType="com.example.security3.entity.SysRole">
  3. select r.<include refid="Base_Column_List" />
  4. from SYS_ROLE r
  5. JOIN SYS_USER_ROLE ur on ur.ROLE_ID = r.ID
  6. WHERE ur.USER_ID = #{userId}
  7. </select>

      UserDetailsService中的loadUserByUsername方法的参数就是用户在登录的时候传入的用户名,根据用户名去查询用户信息(查出来之后,系统会自动进行密码比对)

然后引入security依赖,并配置SecurityConfig,可以采用之前的配置,不过需要稍稍修改一下

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-security</artifactId>
  4. </dependency>

SecurityConfig配置

  1. @Configuration
  2. public class SecurityConfig extends WebSecurityConfigurerAdapter {
  3. public static final String CONTENT_TYPE = "application/json;charset=utf-8";
  4. @Autowired
  5. private SysUserDetailService sysUserDetailService;
  6. @Bean
  7. PasswordEncoder passwordEncoder() {
  8. return new BCryptPasswordEncoder(10);//BCryptPasswordEncoder加密
  9. }
  10. @Override
  11. protected void configure(AuthenticationManagerBuilder auth) throws Exception {
  12. auth.userDetailsService(sysUserDetailService).passwordEncoder(passwordEncoder());
  13. }
  14. @Override
  15. public void configure(WebSecurity web) throws Exception {
  16. web.ignoring().antMatchers("/h2/**");
  17. }
  18. @Override
  19. protected void configure(HttpSecurity http) throws Exception {
  20. http.cors().and()
  21. .authorizeRequests()
  22. .antMatchers("/user/**").hasRole("user")
  23. .antMatchers("/root/**").hasRole("root")
  24. .anyRequest().authenticated()
  25. .and()
  26. .formLogin()
  27. .successHandler((req, resp, auth) -> {
  28. resp.setContentType(CONTENT_TYPE);
  29. PrintWriter out = resp.getWriter();
  30. out.write(JSON.toJSONString(ResponseDTO.success("登录成功!")));
  31. out.flush();
  32. out.close();
  33. })
  34. .failureHandler((req, resp, exception) -> {
  35. resp.setContentType(CONTENT_TYPE);
  36. PrintWriter out = resp.getWriter();
  37. String exeMsg = "登录失败!";
  38. if (exception instanceof LockedException) {
  39. exeMsg = "账户已被锁定!";
  40. } else if (exception instanceof CredentialsExpiredException) {
  41. exeMsg = "密码已过期!";
  42. } else if (exception instanceof AccountExpiredException) {
  43. exeMsg = "账户已过期!";
  44. } else if (exception instanceof DisabledException) {
  45. exeMsg = "账户已被禁用!";
  46. } else if (exception instanceof BadCredentialsException) {
  47. exeMsg = "用户名或者密码输入错误,请重新输入!";
  48. }
  49. out.write(JSON.toJSONString(ResponseDTO.error(exeMsg)));
  50. out.flush();
  51. out.close();
  52. })
  53. .and()
  54. .logout()
  55. .logoutSuccessHandler((req, resp, auth) -> {
  56. resp.setContentType(CONTENT_TYPE);
  57. PrintWriter out = resp.getWriter();
  58. out.write(JSON.toJSONString(ResponseDTO.success("注销成功!再见!")));
  59. out.flush();
  60. out.close();
  61. })
  62. .permitAll()
  63. .and()
  64. .exceptionHandling()
  65. .authenticationEntryPoint((req, resp, exception) -> {
  66. resp.setContentType(CONTENT_TYPE);
  67. PrintWriter out = resp.getWriter();
  68. out.write(JSON.toJSONString(ResponseDTO.unauthenticated("未登录,请重新登录!")));
  69. out.flush();
  70. out.close();
  71. })
  72. .accessDeniedHandler((req, resp, exception) -> {
  73. resp.setContentType(CONTENT_TYPE);
  74. PrintWriter out = resp.getWriter();
  75. out.write(JSON.toJSONString(ResponseDTO.unauthorized("无权限!")));
  76. out.flush();
  77. out.close();
  78. })
  79. .and().csrf().disable();
  80. }
  81. @Bean
  82. RoleHierarchy roleHierarchy() {
  83. RoleHierarchyImpl hierarchy = new RoleHierarchyImpl();
  84. hierarchy.setHierarchy("ROLE_root > ROLE_user");
  85. return hierarchy;
  86. }
  87. }

其中:

      configure(AuthenticationManagerBuilder auth)方法我们使用了自己定义的UserDetailService,并使用了BCryptPasswordEncoder将密码加密

3.启动测试

启动之前,要创建测试类

  1. @RestController
  2. public class TestController {
  3. @GetMapping("/test")
  4. public String test() {
  5. return "Hello World!";
  6. }
  7. @GetMapping("/user/test")
  8. public String user(){
  9. return "user权限";
  10. }
  11. @GetMapping("/root/test")
  12. public String root(){
  13. return "root权限";
  14. }
  15. }

启动类中要记得添加MapperScan

  1. @SpringBootApplication
  2. @MapperScan("com.example.security3.dao")
  3. public class Security3Application {
  4. public static void main(String[] args) {
  5. SpringApplication.run(Security3Application.class, args);
  6. }
  7. }

然后启动项目,对接口进行测试

注意,这里我初始化的密码是123456加密之后的,可自行加密

  1. public static void main(String[] args) {
  2. BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder(10);
  3. System.out.println(bCryptPasswordEncoder.encode("123456"));
  4. }

另外test1用户在初始化时STATUS设置了0,在登陆验证时UserDetails的isEnabled返回了false,所以认为该账户被禁用,抛出DisabledException异常,被failureHandler捕获之后返回了自定义的结果。

  1. @Override
  2. public boolean isEnabled() {return Objects.equals(1, user.getStatus());}

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/67666
推荐阅读
相关标签
  

闽ICP备14008679号