Spring Security框架 
 
第一章了解Spring Security 
 
Spring Security是基于Spring的安全框架。它提供全面的安全性解决方案,同时在Web请求和方法调用级处理身份确认和授权。在Spring Framework基础上,Spring Security充分利用了依赖注入(DI)和面向切面编程(AOP)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。是一个轻量级的安全框架。他与SpringMVC有很好地集成。
1.1Spring Security 核心功能 
(1)认证(你是谁,用户/设备/系统)
(2)验证(你能干什么,也叫权限控制/授权,允许执行的操作)
1.2Spring Security 原理 
基于 Filter,Servlet,AOP实现身份认证和权限验证
第二章 实例驱动学习 
 
导入依赖: 
1 2 3 4 <dependency >     <groupId > org.springframework.boot</groupId >      <artifactId > spring-boot-starter-security</artifactId >  </dependency > 
 
SpringSecurity完整流程 
SpringSecurity的原理其实就是一个过滤链,内部包含了提供各种功能的过滤器。这里我们可以看看入门案例中的过滤器。
 
图中只展示了核心过滤器,其他的非核心过滤器并没有在图中展示。
UsernamePasswordAuthenticationFilter:负责处理我们登录页面填写了用户名密码后的登录请求。入门案例的认证工作主要有它负责。
ExceptionTranslationFilter:处理过滤器中抛出的任何AccessDeniedException和AuthenticationException。
FilterSecurityInterceptor:负责权限的过滤器。
=
初始默认密码登录 
在初次导入框架使用的时候,Security会默认生成一个密码:
这个密码在默认访问任意接口的时候都需要你进行登录操作,登录时账号就为:user,密码就是上面生成的随机密码。
修改默认密码 
修改默认密码后就不会再随机生成密码了,并且也不会展示,修改密码需要在application配置文件里面设置:
1 2 3 4 5 spring:   security:      user:        name:  cbq          password:  cbq    
 
关闭验证 
关闭验证只需要在SpringBoot启动类注解里面添加排除项即可:
1 2 3 4 5 6 7 8 9 10 @SpringBootApplication(exclude = {SecurityAutoConfiguration.class}) public  class  SecurityStudyApplication  {    public  static  void  main (String[] args)  {         SpringApplication.run(SecurityStudyApplication.class, args);     } } 
 
使用内存中的用户信息 
这里一定要注意!!!!
在Spring Security 5.7.0-M2 ,我们弃用了 WebSecurityConfigurerAdapter ,因为我们鼓励用户转向使用基于组件的安全配置。
 
如果是使用了5.7以下的那么就按照以下方法来!!!
使用:WebSecurityConfigurerAdapter控制安全管理的内容。
需要做的使用:继承WebSecurityConfigurerAdapter,重新方法。实现自定义的认证信息。
注解:
1、@Configuration:表示当前类是一个配置类(相当于是Spring的xml配置文件),在这个类方法的返回值是Java对象,这些对象放入到Spring容器中。
2、@EnableWebSecurity:表示启动Spring Security安全框架的功能。
3/@Bean:把方法返回值的对象,放入到Spring容器中。
这里注意Security5中密码必须要加密,不然报错。
1 java.lang.IllegalArgumentException: There is no PasswordEncoder mapped foor the id "null"  
 
 
如果使用了新版Security那么:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 @Configuration public  class  SecurityConfiguration  {       @Bean      public  UserDetailsService userDetailsService () {         UserDetails  user1  =  User.builder().                 username("cbq1" ).                 password("123456" )                 .roles("student" )                 .build();         UserDetails  user2  =  User.builder()                 .username("cbq2" )                 .password("123456" )                 .roles("teacher" )                 .build();         InMemoryUserDetailsManager  memoryUserDetailsManager  =  new  InMemoryUserDetailsManager ();         memoryUserDetailsManager.createUser(user1);         memoryUserDetailsManager.createUser(user2);         return  memoryUserDetailsManager;     } } 
 
注意 :在这些例子中,我们为了可读性使用了User.withDefaultPasswordEncoder()。这不适合生产项目,我们建议在生产项目中使用散列密码。请按照参考文档 所说的用Spring Boot命令行工具来做。
基于角色Role的身份认证,同一个用户可以有不同的角色。同时可以开启对方法级别的认证。 
以下是WebSecurity.java配置文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 import  org.springframework.context.annotation.Bean;import  org.springframework.context.annotation.Configuration;import  org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;import  org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;import  org.springframework.security.core.userdetails.User;import  org.springframework.security.core.userdetails.UserDetails;import  org.springframework.security.provisioning.InMemoryUserDetailsManager;@Configuration @EnableWebSecurity @EnableMethodSecurity(prePostEnabled = true)  public  class  MyWebSecurityConfig  {         @Bean      public  InMemoryUserDetailsManager userDetailsManager () {         UserDetails  user1  =  User.withDefaultPasswordEncoder()                 .username("cbq" )                 .password("cbq" )                 .roles("normal" )                 .build();         UserDetails  user2  =  User.withDefaultPasswordEncoder()                 .username("cbq1" )                 .password("cbq1" )                 .roles("normal" )                 .build();         UserDetails  user3  =  User.withDefaultPasswordEncoder()                 .username("cbq2" )                 .password("cbq2" )                 .roles("normal" ,"admin" )                 .build();         return  new  InMemoryUserDetailsManager (user1,user2,user3);     } } 
 
这里开了三个用户,一共两个角色,一个normal,一个admin。一个用户可以拥有多个角色。基于角色的访问需要配合@PreAuthorize注解使用,详细如下,这里@PreAuthorize注解标注了哪个接口只能哪几个角色能访问。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import  org.springframework.security.access.prepost.PreAuthorize;import  org.springframework.web.bind.annotation.RequestMapping;import  org.springframework.web.bind.annotation.RestController;@RestController @RequestMapping("hello") public  class  HelloSecurityController  {    @RequestMapping("world")      @PreAuthorize(value = "hasAnyRole('admin','normal')")      public  Object sayHello () {         return  "拥有admin和normal" ;     }     @RequestMapping("hhh")      @PreAuthorize(value = "hasAnyRole('admin')")      public  Object hhh () {         return  "admin角色才可以访问" ;     } } 
 
关于authorities设置权限与roles设置角色的坑 
在实验中发现,如果同时使用roles设置用户的角色,用authoritites设置用户的权限,谁写在最后那么就用谁作为最终的权限标志。比如以下例子:
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 @Bean public  UserDetailsService userDetailsService () {    UserDetails  user1  =  User.builder().             username("cbq1" ).             password(passwordBCryptEncoder().encode("123456" ))             .roles("student" )             .authorities("student:add" ,"student:delete" )             .build();     UserDetails  user2  =  User.builder()             .username("cbq2" )             .password(passwordBCryptEncoder().encode("123456" ))         	.authorities("student:add" ,"student:delete" )             .roles("teacher" )             .build();     InMemoryUserDetailsManager  memoryUserDetailsManager  =  new  InMemoryUserDetailsManager ();     memoryUserDetailsManager.createUser(user1);     memoryUserDetailsManager.createUser(user2);     return  memoryUserDetailsManager; } 
 
在上述代码中,user1角色的权限最终为student:add与student:delete。user2角色的权限最终为ROLE_teacher角色标志。
基于JDBC的用户认证 
从数据库Mysql中获取用户的身份信息(用户名称,密码,角色)
在Spring Security框架对象用户信息的表示类是UserDetails.
UserDetails是一个接口,高度抽象的用户信息类(相当于项目中的User类)
User类:是UserDetails接口的实现类,构造方法有三个参数:
username,password,authorities
需要向Spring Security提供User对象,这个对象的数据来自数据库中的查询。
实现UserDetailsService接口,重写UserDetails loadUserByUsername(String var1)在方法中获取数据库中的用户信息,也就是执行数据库的查询,条件是用户名称。
密码加密存储 
实际项目中我们不会把密码明文存储在数据库表中。
在SpringSecurity中明文加密为:NoOpPasswordEncoder.getInstance();
默认使用的PasswordEncoder要求数据库中的密码格式为:{id}password。它会根据id去判断密码的加密方式。但是我们一般不会采用这种方式。所以就需要替换PordEncoder。
我们一般使用SpringSecurity为我们提供的BCryptPasswordEncoder。
我们只需要使用把BCryptPasswordEncoder对象注入Spring容器中,SpringSecurity就会使用该PasswordEncoder来进行密码校验。
我们可以定义一个SpringSecurity的配置类,SpringSecurity要求这个配置类要继承WebSecurityConfigurerAdapter。
第三章 权限控制 
 
基于访问路径进行访问权限控制 
拦截相应没有访问权限访问页面的用户,注意authorizeRequests与authorizeHttpRequests不能混用,不然报错。
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 import  jakarta.annotation.Resource;import  org.springframework.context.annotation.Bean;import  org.springframework.context.annotation.Configuration;import  org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;import  org.springframework.security.config.annotation.web.builders.HttpSecurity;import  org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;import  org.springframework.security.core.userdetails.UserDetailsService;import  org.springframework.security.web.SecurityFilterChain;@Configuration @EnableWebSecurity @EnableMethodSecurity(prePostEnabled = true) public  class  MyWebSecurityConfig  {    @Resource      private  UserDetailsService userDetailsService;     @Bean      SecurityFilterChain securityFilterChain (HttpSecurity httpSecurity)  throws  Exception {                  httpSecurity.authorizeHttpRequests().requestMatchers("/login" ,"register" ,"/upLogin" ).permitAll()                                  .requestMatchers("/success" ).hasAnyRole("USER" ).and()                          .formLogin().loginPage("/loginSelf" ).loginProcessingUrl("upLogin" )                 .usernameParameter("username" ).passwordParameter("password" );         return  httpSecurity.build();     } } 
 
以下为新版本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 import  org.springframework.context.annotation.Configuration;import  org.springframework.security.config.annotation.web.builders.HttpSecurity;import  org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;@Configuration public  class  WebSecurityConfig  extends  WebSecurityConfigurerAdapter  {    @Override      protected  void  configure (HttpSecurity http)  throws  Exception {         http.authorizeRequests()                                                   .mvcMatchers("/student/**" )                                                                     .hasAnyAuthority("student:add" )                  .mvcMatchers("/teacher/**" )                  .hasAuthority("ROLE_teacher" )                  .anyRequest()                  .authenticated();          http.formLogin().permitAll();      } } 
 
匹配路径url的写法有三种,判断权限有五种。
基于方法来进行访问权限控制 
基于方法粒度的权限校验一定要记得在Security配置类中加上@EnableGlobalMethodSecurity注解设置prePostEnabled=true参数,否则直接使用注解配置权限将会无效!!!
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 import  org.springframework.context.annotation.Bean;import  org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;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.crypto.bcrypt.BCryptPasswordEncoder;import  org.springframework.security.crypto.password.PasswordEncoder;import  org.springframework.security.provisioning.InMemoryUserDetailsManager;@EnableGlobalMethodSecurity(prePostEnabled = true)  public  class  SecurityUserConfig  {    @Bean      public  UserDetailsService userDetailsService () {         UserDetails  u1  =  User.builder()                 .username("cbq1" )                 .password(passwordEncoder().encode("123456" ))                 .roles("student" )                 .build();         UserDetails  u2  =  User.builder()                 .username("cbq2" )                 .password(passwordEncoder().encode("123456" ))                 .authorities("teacher:update" ,"teacher:query" )                 .build();         InMemoryUserDetailsManager  inMemoryUserDetailsManager  =  new  InMemoryUserDetailsManager ();         inMemoryUserDetailsManager.createUser(u1);         inMemoryUserDetailsManager.createUser(u2);         return  inMemoryUserDetailsManager;     }     @Bean      public  PasswordEncoder passwordEncoder () {         return  new  BCryptPasswordEncoder ();     } } 
 
配置完之后即可使用了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 import  org.springframework.security.access.prepost.PreAuthorize;import  org.springframework.web.bind.annotation.GetMapping;import  org.springframework.web.bind.annotation.RequestMapping;import  org.springframework.web.bind.annotation.RestController;@RestController @RequestMapping("teacher") public  class  TeacherController  {    @GetMapping("add")      @PreAuthorize("hasRole('teacher')")       public  Object add () {         return  "添加成功" ;     }     @GetMapping("delete")      @PreAuthorize("hasRole('teacher')")      public  Object delete () {         return  "删除成功" ;     }     @GetMapping("update")      @PreAuthorize("hasRole('teacher') or hasAuthority('teacher:update')")      public  Object update () {         return  "更新成功" ;     } } 
 
第四章 利用处理器返回JSON 
设置登录成功返回json 
设置登录成功返回json创建一个类并且实现AuthenticationSuccessHandler接口,
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 import  com.example.springsecuritystudy3.vo.HttpResult;import  com.fasterxml.jackson.databind.ObjectMapper;import  lombok.extern.slf4j.Slf4j;import  org.springframework.security.core.Authentication;import  org.springframework.security.web.authentication.AuthenticationSuccessHandler;import  org.springframework.stereotype.Component;import  javax.annotation.Resource;import  javax.servlet.ServletException;import  javax.servlet.http.HttpServletRequest;import  javax.servlet.http.HttpServletResponse;import  java.io.IOException;import  java.io.PrintWriter;@Slf4j @Component public  class  AppAuthenticationSuccessHandler  implements  AuthenticationSuccessHandler  {    @Resource      private  ObjectMapper objectMapper;     @Override      public  void  onAuthenticationSuccess (HttpServletRequest request, HttpServletResponse response, Authentication authentication)  throws  IOException, ServletException {         HttpResult  httpResult  =  HttpResult.builder()                 .code(1 )                 .msg("登录成功" )                 .build();         String  responseJson  =  objectMapper.writeValueAsString(httpResult);         response.setCharacterEncoding("UTF-8" );         response.setContentType("application/json;charset=utf-8" );         PrintWriter  writer  =  response.getWriter();         writer.println(responseJson);         writer.flush();     } } 
 
实现接口之后只需要Security配置类当中配置好即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 import  lombok.extern.slf4j.Slf4j;import  org.springframework.context.annotation.Configuration;import  org.springframework.security.config.annotation.web.builders.HttpSecurity;import  org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;import  javax.annotation.Resource;@Configuration @Slf4j public  class  WebSecurityConfig  extends  WebSecurityConfigurerAdapter  {    @Resource      AppAuthenticationSuccessHandler appAuthenticationSuccessHandler;     @Resource      AppAuthenticationFailureHandler appAuthenticationFailureHandler;     @Override      protected  void  configure (HttpSecurity http)  throws  Exception {         http.authorizeRequests()                 .anyRequest()                 .authenticated();                  http.formLogin()                 .successHandler(appAuthenticationSuccessHandler)                  .failureHandler(appAuthenticationFailureHandler)                  .permitAll();     } } 
 
设置退出登录成功返回json 
设置退出登录成功返回json创建一个类并且实现LogoutSuccessHandler接口,
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 import  com.example.springsecuritystudy3.vo.HttpResult;import  com.fasterxml.jackson.databind.ObjectMapper;import  org.springframework.security.core.Authentication;import  org.springframework.security.web.authentication.logout.LogoutSuccessHandler;import  org.springframework.stereotype.Component;import  javax.annotation.Resource;import  javax.servlet.ServletException;import  javax.servlet.http.HttpServletRequest;import  javax.servlet.http.HttpServletResponse;import  java.io.IOException;import  java.io.PrintWriter;@Component public  class  AppLogoutSuccessHandler  implements  LogoutSuccessHandler  {    @Resource      ObjectMapper objectMapper;     @Override      public  void  onLogoutSuccess (HttpServletRequest request, HttpServletResponse response, Authentication authentication)  throws  IOException, ServletException {         HttpResult  httpResult  =  HttpResult.builder()                 .code(0 )                 .msg("退出登录成功" )                 .build();         String  json  =  objectMapper.writeValueAsString(httpResult);         response.setCharacterEncoding("UTF-8" );         response.setContentType("application/json;charset=utf-8" );         PrintWriter  printWriter  =  response.getWriter();         printWriter.println(json);         printWriter.flush();     } } 
 
设置登录失败返回json 
设置登录失败返回json创建一个类并且实现AuthenticationSuccessHandler接口,
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 import  com.example.springsecuritystudy3.vo.HttpResult;import  com.fasterxml.jackson.databind.ObjectMapper;import  lombok.extern.slf4j.Slf4j;import  org.springframework.security.core.AuthenticationException;import  org.springframework.security.web.authentication.AuthenticationFailureHandler;import  org.springframework.stereotype.Component;import  javax.annotation.Resource;import  javax.servlet.ServletException;import  javax.servlet.http.HttpServletRequest;import  javax.servlet.http.HttpServletResponse;import  java.io.IOException;import  java.io.PrintWriter;@Slf4j @Component public  class  AppAuthenticationFailureHandler  implements  AuthenticationFailureHandler  {    @Resource      ObjectMapper objectMapper;     @Override      public  void  onAuthenticationFailure (HttpServletRequest request, HttpServletResponse response, AuthenticationException exception)  throws  IOException, ServletException {         HttpResult  httpResult  =  HttpResult.builder()                 .code(0 )                 .msg("登录失败" )                 .build();         String  json  =  objectMapper.writeValueAsString(httpResult);         response.setCharacterEncoding("UTF-8" );         response.setContentType("application/json;charset=utf-8" );         PrintWriter  printWriter  =  response.getWriter();         printWriter.println(json);         printWriter.flush();     } } 
 
设置访问拒绝返回json 
设置访问拒绝返回json创建一个类并且实现AuthenticationSuccessHandler接口,
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 import  com.example.springsecuritystudy3.vo.HttpResult;import  com.fasterxml.jackson.databind.ObjectMapper;import  org.springframework.security.access.AccessDeniedException;import  org.springframework.security.web.access.AccessDeniedHandler;import  org.springframework.stereotype.Component;import  javax.annotation.Resource;import  javax.servlet.ServletException;import  javax.servlet.http.HttpServletRequest;import  javax.servlet.http.HttpServletResponse;import  java.io.IOException;import  java.io.PrintWriter;@Component public  class  AppAccessDenyHandler  implements  AccessDeniedHandler  {    @Resource      ObjectMapper objectMapper;     @Override      public  void  handle (HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException)  throws  IOException, ServletException {         HttpResult  httpResult  =  HttpResult.builder()                 .code(2 )                 .msg("您没有权限访问" )                 .build();         String  json  =  objectMapper.writeValueAsString(httpResult);         response.setCharacterEncoding("UTF-8" );         response.setContentType("application/json;charset=utf-8" );         PrintWriter  printWriter  =  response.getWriter();         printWriter.println(json);         printWriter.flush();     } } 
 
实现接口之后只需要Security配置类当中配置好即可。 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 import  lombok.extern.slf4j.Slf4j;import  org.springframework.context.annotation.Configuration;import  org.springframework.security.config.annotation.web.builders.HttpSecurity;import  org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;import  javax.annotation.Resource;@Configuration @Slf4j public  class  WebSecurityConfig  extends  WebSecurityConfigurerAdapter  {    @Resource      AppAuthenticationSuccessHandler appAuthenticationSuccessHandler;     @Resource      AppAuthenticationFailureHandler appAuthenticationFailureHandler;     @Resource      AppLogoutSuccessHandler appLogoutSuccessHandler;     @Resource      AppAccessDenyHandler appAccessDenyHandler;     @Override      protected  void  configure (HttpSecurity http)  throws  Exception {         http.authorizeRequests()                 .anyRequest()                 .authenticated();                  http.formLogin()                 .successHandler(appAuthenticationSuccessHandler)                  .failureHandler(appAuthenticationFailureHandler)                  .permitAll();         http.logout().logoutSuccessHandler(appLogoutSuccessHandler);          http.exceptionHandling().accessDeniedHandler(appAccessDenyHandler);      } } 
 
通过实现UserDetails接口来定义用户 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 import  com.example.springsecuritystudy4.entity.User;import  org.springframework.security.core.GrantedAuthority;import  org.springframework.security.core.authority.SimpleGrantedAuthority;import  org.springframework.security.core.userdetails.UserDetails;import  java.util.Collection;import  java.util.List;public  class  SecurityUser  implements  UserDetails  {    private  final  User user;     private  List<SimpleGrantedAuthority> authorityList;     public  SecurityUser (User user)  {         this .user = user;     }     @Override      public  Collection<? extends  GrantedAuthority > getAuthorities() {         return  authorityList;     }     public  void  setAuthorityList (List<SimpleGrantedAuthority> authorityList)  {         this .authorityList = authorityList;     }     @Override      public  String getPassword ()  {         String  pwd  =  user.getPassword();         user.setPassword(null );         return  pwd;     }     @Override      public  String getUsername ()  {         return  user.getUsername();     }     @Override      public  boolean  isAccountNonExpired ()  {         return  user.getAccountNoExpired().equals(1 );     }     @Override      public  boolean  isAccountNonLocked ()  {         return  user.getAccountNoLocked().equals(1 );     }     @Override      public  boolean  isCredentialsNonExpired ()  {         return  user.getCredentialsNoExpired().equals(1 );     }     @Override      public  boolean  isEnabled ()  {         return  true ;     } } 
 
第五章 基于数据库的认证 
 
1、第一步则是先要实现UserDetails接口 
主要用于定制化将数据库数据对接Security框架。
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 import  com.example.springsecuritystudy4.entity.User;import  org.springframework.security.core.GrantedAuthority;import  org.springframework.security.core.authority.SimpleGrantedAuthority;import  org.springframework.security.core.userdetails.UserDetails;import  java.util.Collection;import  java.util.List;public  class  SecurityUser  implements  UserDetails  {    private  final  User user;     private  List<SimpleGrantedAuthority> authorityList;     public  SecurityUser (User user)  {         this .user = user;     }     @Override      public  Collection<? extends  GrantedAuthority > getAuthorities() {         return  authorityList;     }     public  void  setAuthorityList (List<SimpleGrantedAuthority> authorityList)  {         this .authorityList = authorityList;     }     @Override      public  String getPassword ()  {         String  pwd  =  user.getPassword();         user.setPassword(null );         return  pwd;     }     @Override      public  String getUsername ()  {         return  user.getUsername();     }          @Override      public  boolean  isAccountNonExpired ()  {         return  user.getAccountNoExpired().equals(1 );     }               @Override      public  boolean  isAccountNonLocked ()  {         return  user.getAccountNoLocked().equals(1 );     }       @Override      public  boolean  isCredentialsNonExpired ()  {         return  user.getCredentialsNoExpired().equals(1 );     }       @Override      public  boolean  isEnabled ()  {         return  true ;     } } 
 
2、实现UserDetailsService接口 
实现UserDetailService接口的作用主要就是定制化用户登录时的数据获取方式以及对比方式等。
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 import  com.example.springsecuritystudy4.entity.User;import  com.example.springsecuritystudy4.service.RoleMenuService;import  com.example.springsecuritystudy4.service.UserService;import  com.example.springsecuritystudy4.vo.SecurityUser;import  org.springframework.security.core.authority.SimpleGrantedAuthority;import  org.springframework.security.core.userdetails.UserDetails;import  org.springframework.security.core.userdetails.UserDetailsService;import  org.springframework.security.core.userdetails.UsernameNotFoundException;import  org.springframework.stereotype.Service;import  javax.annotation.Resource;import  java.util.ArrayList;import  java.util.List;@Service public  class  SecurityUserDetailServiceImpl  implements  UserDetailsService  {    @Resource      private  UserService userService;     @Resource      private  RoleMenuService roleMenuService;     @Override      public  UserDetails loadUserByUsername (String username)  throws  UsernameNotFoundException {         User  byUserName  =  userService.getByUserName(username);         if (byUserName==null )             throw  new  UsernameNotFoundException ("该用户不存在" );         List<String> queryPermissionsByUserId = roleMenuService.queryPermissionsByUserId(byUserName.getUid());         SecurityUser  securityUser  =  new  SecurityUser (byUserName);         List<SimpleGrantedAuthority> authorityList = new  ArrayList <>();         queryPermissionsByUserId.stream().forEach((value)-> authorityList.add(new  SimpleGrantedAuthority (value)));         securityUser.setAuthorityList(authorityList);         return  securityUser;     } } 
 
第六章 WebSecurity配置中关于其中登录页面等其他页面定制化 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 import  lombok.extern.slf4j.Slf4j;import  org.springframework.context.annotation.Bean;import  org.springframework.context.annotation.Configuration;import  org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;import  org.springframework.security.config.annotation.web.builders.HttpSecurity;import  org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;import  org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;import  org.springframework.security.crypto.password.PasswordEncoder;@Slf4j @Configuration @EnableGlobalMethodSecurity(prePostEnabled = true) public  class  WebSecurityConfig  extends  WebSecurityConfigurerAdapter  {    @Bean      public  PasswordEncoder passwordEncoder () {         return  new  BCryptPasswordEncoder ();     }     @Override      protected  void  configure (HttpSecurity http)  throws  Exception {         http.authorizeRequests()                 .anyRequest()                 .authenticated();         http.formLogin()                 .loginPage("/toLogin" )                  .usernameParameter("uname" )                  .passwordParameter("pwd" )                  .loginProcessingUrl("/login/doLogin" )                   .failureForwardUrl("/toLogin" )                  .successForwardUrl("/toIndex" )                  .permitAll();           http.logout().logoutSuccessUrl("/toLogin" );           http.csrf().disable();      } } 
 
第七章 验证码整合 
 
验证码生成工具可以使用hutool依赖库进行。
 
验证码生成接口代码 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 import  cn.hutool.captcha.CaptchaUtil;import  cn.hutool.captcha.CircleCaptcha;import  cn.hutool.captcha.generator.MathGenerator;import  lombok.extern.slf4j.Slf4j;import  org.springframework.stereotype.Controller;import  org.springframework.web.bind.annotation.GetMapping;import  javax.imageio.ImageIO;import  javax.servlet.http.HttpServletRequest;import  javax.servlet.http.HttpServletResponse;import  java.io.IOException;@Slf4j @Controller public  class  CaptchaController  {    @GetMapping(value = "code/getCaptchaCode")      public  void  getCaptchaCode (HttpServletRequest httpServletRequest,                                 HttpServletResponse httpServletResponse)  throws  IOException {                 CircleCaptcha  circleCaptcha  =  CaptchaUtil.createCircleCaptcha(200 , 100 , 4 , 1000 );         circleCaptcha.setGenerator(new  MathGenerator ());         String  code  =  circleCaptcha.getCode();         log.info("生成的图片验证码为:{}" ,code);                  httpServletRequest.getSession().setAttribute("CAPTCHA CODE" ,code);         httpServletResponse.setContentType("image/png" );                  ImageIO.write(circleCaptcha.getImage(),"PNG" ,httpServletResponse.getOutputStream());     } } 
 
写好验证码之后即可在WebSecurity实现配置类中将验证码接口放开:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 import  com.example.springsecuritystudy4.filter.ValidateCodeFilter;import  lombok.extern.slf4j.Slf4j;import  org.springframework.context.annotation.Bean;import  org.springframework.context.annotation.Configuration;import  org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;import  org.springframework.security.config.annotation.web.builders.HttpSecurity;import  org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;import  org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;import  org.springframework.security.crypto.password.PasswordEncoder;import  org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;import  javax.annotation.Resource;@Slf4j @Configuration @EnableGlobalMethodSecurity(prePostEnabled = true) public  class  WebSecurityConfig  extends  WebSecurityConfigurerAdapter  {    @Bean      public  PasswordEncoder passwordEncoder () {         return  new  BCryptPasswordEncoder ();     }     @Override      protected  void  configure (HttpSecurity http)  throws  Exception {         http.authorizeRequests()                 .mvcMatchers("/code/getCaptchaCode" )                  .permitAll()                 .anyRequest()                 .authenticated();         http.formLogin()                 .loginPage("/toLogin" )                  .usernameParameter("uname" )                  .passwordParameter("pwd" )                  .loginProcessingUrl("/login/doLogin" )                   .failureForwardUrl("/toLogin" )                  .successForwardUrl("/toIndex" )                  .permitAll();           http.logout().logoutSuccessUrl("/toLogin" );           http.csrf().disable();      } } 
 
利用过滤器来验证验证码 
过滤器需要继承实现OncePerRequestFilter接口。以下为实现例子
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 import  lombok.extern.slf4j.Slf4j;import  org.springframework.stereotype.Component;import  org.springframework.util.StringUtils;import  org.springframework.web.filter.OncePerRequestFilter;import  javax.servlet.FilterChain;import  javax.servlet.ServletException;import  javax.servlet.http.HttpServletRequest;import  javax.servlet.http.HttpServletResponse;import  java.io.IOException;@Slf4j @Component public  class  ValidateCodeFilter  extends  OncePerRequestFilter  {    @Override      protected  void  doFilterInternal (HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)  throws  ServletException, IOException {                  String  requestURL  =  request.getRequestURI();         if (!requestURL.equals("/login/doLogin" )){              doFilter(request,response,filterChain);         }     }     private  void  validateCode (HttpServletRequest request,HttpServletResponse response,FilterChain filterChain)  throws  ServletException, IOException {                  String  code  =  request.getParameter("code" );                  String  captchaCode  =  (String) request.getSession().getAttribute("CAPTCHA_CODE" );                  if (StringUtils.isEmpty(code)){             request.getSession().setAttribute("captcha_code_error" ,"请输入验证码" );             return ;         }         if (StringUtils.isEmpty(captchaCode)){             request.getSession().setAttribute("captcha_code_error" ,"验证码错误" );         }         request.getSession().setAttribute("CAPTCHA_CODE" ,"" );         this .doFilter(request,response,filterChain);     } } 
 
写好过滤器后即可在配置类里面使用过滤器:
 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 import  com.example.springsecuritystudy4.filter.ValidateCodeFilter;import  lombok.extern.slf4j.Slf4j;import  org.springframework.context.annotation.Bean;import  org.springframework.context.annotation.Configuration;import  org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;import  org.springframework.security.config.annotation.web.builders.HttpSecurity;import  org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;import  org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;import  org.springframework.security.crypto.password.PasswordEncoder;import  org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;import  javax.annotation.Resource;@Slf4j @Configuration @EnableGlobalMethodSecurity(prePostEnabled = true) public  class  WebSecurityConfig  extends  WebSecurityConfigurerAdapter  {    @Resource      ValidateCodeFilter validateCodeFilter;     @Bean      public  PasswordEncoder passwordEncoder () {         return  new  BCryptPasswordEncoder ();     }     @Override      protected  void  configure (HttpSecurity http)  throws  Exception {                  http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class);         http.authorizeRequests()                 .mvcMatchers("/code/getCaptchaCode" )                 .permitAll()                 .anyRequest()                 .authenticated();         http.formLogin()                 .loginPage("/toLogin" )                  .usernameParameter("uname" )                  .passwordParameter("pwd" )                  .loginProcessingUrl("/login/doLogin" )                   .failureForwardUrl("/toLogin" )                  .successForwardUrl("/toIndex" )                  .permitAll();           http.logout().logoutSuccessUrl("/toLogin" );           http.csrf().disable();      } } 
 
第八章 SpringSecurity+JWT实现前后端分离 
 
这里JWT相关的工具类代码就不多展示了。这边可以在网上找到很多的JWTUtils模板。
 
静态文件展示: 
对于前端我们写一个用于测试的几个页面。
 
1、登录页面: 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 <!DOCTYPE html > <html  lang ="en" >     <head >          <meta  charset ="UTF-8" >          <meta  name ="viewport"  content ="width=device-width, initial-scale=1.0" >          <title > 登录</title >          <script  src ="./script/jquery-3.7.1.min.js" > </script >      </head >      <body >          <table >              <tr >                  <td > <input  id ="act"  type ="text" > </td >              </tr >              <tr >                  <td > <input  id ="psd"  type ="password" > </td >              </tr >              <td > <button  id ="login" > 登录</button > <button > 注册</button > </td >          </table >      </body >      <script >      var  act = document .getElementById ("act" );     var  psw = document .getElementById ("psd" );         $("#login" ).click (function ( ){             var  act = $('#act' ).val ();             var  psw = $('#psd' ).val ();             $.ajax ('login' ,{                 method : 'POST' ,                 contentType : 'application/json' ,                 dataType : 'json' ,                 data : JSON .stringify ({uid : act,password : psw}),                 success :function  (result ){                     alert (JSON .stringify (result))                     console .log (result)                 },                 error :function  (result ){                     alert (result.data )                 }             })         }); </script > </html > 
 
2、主页: 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 <!DOCTYPE html > <html  lang ="en" > <head >     <meta  charset ="UTF-8" >      <meta  name ="viewport"  content ="width=device-width, initial-scale=1.0" >      <title > 管理页面</title >  </head > <body >     <table >          <tr >              <td > 测试成功</td >              <td > 测试成功</td >              <td > 测试成功</td >          </tr >          <tr >              <td > 测试成功</td >              <td > 测试成功</td >              <td > 测试成功</td >          </tr >          <tr >              <td > 测试成功</td >              <td > 测试成功</td >              <td > 测试成功</td >          </tr >      </table >  </body > </html > 
 
文件目录展示:
配置SpringSecurity: 
1.1、得到UserDetails方式(实现UserDetailsService接口的方式): 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 import  com.cbq.springsecuritystudy6.entity.User;import  com.cbq.springsecuritystudy6.service.RoleService;import  com.cbq.springsecuritystudy6.service.UserService;import  com.cbq.springsecuritystudy6.vo.SecurityUser;import  org.springframework.security.core.authority.SimpleGrantedAuthority;import  org.springframework.security.core.userdetails.UserDetails;import  org.springframework.security.core.userdetails.UserDetailsService;import  org.springframework.security.core.userdetails.UsernameNotFoundException;import  org.springframework.stereotype.Service;import  javax.annotation.Resource;import  java.util.ArrayList;import  java.util.List;@Service public  class  SecurityUserDetailServiceImpl  implements  UserDetailsService  {    @Resource      UserService userService;     @Resource      private  RoleService roleService;     @Override      public  UserDetails loadUserByUsername (String username)  throws  UsernameNotFoundException {         User  userFromUid  =  userService.getUserFromUid(username);         if (userFromUid==null ){             throw  new  UsernameNotFoundException ("该用户不存在" );         }         List<String> rolePermission = roleService.queryPermissionsByUserUid(username);         SecurityUser  securityUser  =  new  SecurityUser (userFromUid);         ArrayList<SimpleGrantedAuthority> authorityArrayList = new  ArrayList <>();         rolePermission.stream().forEach((v)-> authorityArrayList.add(new  SimpleGrantedAuthority (v)));         securityUser.setAuthorityList(authorityArrayList);         return  securityUser;     } } 
 
1.2、得到UserDetails方式(实现UserDetails接口的方式)(推荐): 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 import  com.cbq.springsecuritystudy6.entity.User;import  org.springframework.security.core.GrantedAuthority;import  org.springframework.security.core.authority.SimpleGrantedAuthority;import  org.springframework.security.core.userdetails.UserDetails;import  java.util.Collection;import  java.util.List;public  class  SecurityUser  implements  UserDetails  {    private  final  User user;     private  List<SimpleGrantedAuthority> authorityList;          public  SecurityUser (User user)  {         this .user = user;     }          @Override      public  Collection<? extends  GrantedAuthority > getAuthorities() {         return  authorityList;     }          public  void  setAuthorityList (List<SimpleGrantedAuthority> authorityList)  {         this .authorityList = authorityList;     }     public  User getUser ()  {         return  user;     }     @Override      public  String getPassword ()  {         String  pws  =  user.getPassword();         user.setPassword(null );         return  pws;     }     @Override      public  String getUsername ()  {         return  user.getUid();     }          @Override      public  boolean  isAccountNonExpired ()  {         return  true ;     }          @Override      public  boolean  isAccountNonLocked ()  {         return  true ;     }          @Override      public  boolean  isCredentialsNonExpired ()  {         return  true ;     }          @Override      public  boolean  isEnabled ()  {         return  true ;     } } 
 
2、通过拦截器接口实现JWT的拦截认证: 
这里就用Map来模拟Redis了。
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 import  com.auth0.jwt.JWT;import  com.auth0.jwt.interfaces.Claim;import  com.auth0.jwt.interfaces.DecodedJWT;import  com.cbq.springsecuritystudy6.utils.JWTUtils;import  com.cbq.springsecuritystudy6.vo.SecurityUser;import  lombok.extern.slf4j.Slf4j;import  org.springframework.security.authentication.UsernamePasswordAuthenticationToken;import  org.springframework.security.core.context.SecurityContextHolder;import  org.springframework.stereotype.Component;import  org.springframework.util.StringUtils;import  org.springframework.web.filter.OncePerRequestFilter;import  javax.servlet.FilterChain;import  javax.servlet.ServletException;import  javax.servlet.http.HttpServletRequest;import  javax.servlet.http.HttpServletResponse;import  java.io.IOException;import  java.util.HashMap;import  java.util.Map;import  java.util.Objects;@Slf4j @Component public  class  JwtCheckFilter  extends  OncePerRequestFilter  {         public  static  Map<String,Object> redis = new  HashMap <>();     @Override      protected  void  doFilterInternal (HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)  throws  ServletException, IOException {                  String  authorization  =  request.getHeader("Authorization" );                  if (!StringUtils.hasText(authorization)){                          filterChain.doFilter(request,response);             return ;         }         DecodedJWT  token  =  JWTUtils.getToken(authorization);                  Claim  uid  =  token.getClaim("uid" );         System.out.println(uid.asString());                  if (!JWTUtils.verify(authorization) && JWTUtils.isExpired(authorization)){             try  {                 throw  new  RuntimeException ("非法Token" );             }catch  (Exception e){             }         }         String  redisKey  =  "login:" +uid.asString();                  SecurityUser  securityUser  = (SecurityUser) redis.get(redisKey);                  if (Objects.isNull(securityUser)){             throw  new  RuntimeException ("您未登录" );         }                  UsernamePasswordAuthenticationToken  usernamePasswordAuthenticationToken  =  new  UsernamePasswordAuthenticationToken (securityUser,null ,securityUser.getAuthorities());         SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);                  filterChain.doFilter(request,response);     } } 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 import  com.cbq.springsecuritystudy6.filter.JwtCheckFilter;import  lombok.extern.slf4j.Slf4j;import  org.springframework.context.annotation.Bean;import  org.springframework.context.annotation.Configuration;import  org.springframework.security.authentication.AuthenticationManager;import  org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;import  org.springframework.security.config.annotation.web.builders.HttpSecurity;import  org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;import  org.springframework.security.config.http.SessionCreationPolicy;import  org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;import  org.springframework.security.crypto.password.PasswordEncoder;import  org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;import  javax.annotation.Resource;@Slf4j @Configuration @EnableGlobalMethodSecurity(prePostEnabled = true) public  class  WebSecurityConfig  extends  WebSecurityConfigurerAdapter  {    @Resource      MyAuthenticationSuccessHandler myAuthenticationSuccessHandler;     @Resource      JwtCheckFilter jwtCheckFilter;     @Bean      public  PasswordEncoder passwordEncoder ()  {         return  new  BCryptPasswordEncoder ();     }     @Bean      @Override      public  AuthenticationManager authenticationManagerBean ()  throws  Exception {         return  super .authenticationManagerBean();     }     @Override      protected  void  configure (HttpSecurity http)  throws  Exception {                  http.csrf().disable();                  http.addFilterBefore(jwtCheckFilter, UsernamePasswordAuthenticationFilter.class);                  http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);         http.authorizeRequests()                 .antMatchers("/**/**.html" ).denyAll()                 .antMatchers("/login/**" ).permitAll()                 .anyRequest()                 .authenticated();         http.formLogin()                 .loginPage("/login/index.html" )                 .permitAll();     } } 
 
好了,到这所有的Security配置基本完成了。现在就是相关的访问接口的实现和对接。
 
实现登录的相关API接口等: 
Controller层 
LoginController
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import  com.cbq.springsecuritystudy6.entity.User;import  com.cbq.springsecuritystudy6.service.LoginService;import  org.springframework.web.bind.annotation.PostMapping;import  org.springframework.web.bind.annotation.RequestBody;import  org.springframework.web.bind.annotation.RequestMapping;import  org.springframework.web.bind.annotation.RestController;import  javax.annotation.Resource;@RestController @RequestMapping("login") public  class  LoginController  {    @Resource      private  LoginService loginService;     @PostMapping("login")      public  Object login (@RequestBody  User user) {         return  loginService.toLogin(user.getUid(),user.getPassword());     } } 
 
UserController
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 import  org.springframework.security.access.prepost.PreAuthorize;import  org.springframework.stereotype.Controller;import  org.springframework.web.bind.annotation.GetMapping;import  org.springframework.web.bind.annotation.RequestMapping;import  org.springframework.web.bind.annotation.ResponseBody;@Controller @RequestMapping("user") public  class  UserController  {    @ResponseBody      @GetMapping("query")      @PreAuthorize("hasAuthority('root:query') or hasRole('ROOT')")      public  Object query () {         System.out.println("成功访问查询页面" );         return  "成功访问查询页面" ;     }     @ResponseBody      @GetMapping("delete")      @PreAuthorize("hasRole('ROOT')")      public  Object delete () {         return  "删除成功" ;     }     @GetMapping("index")      public  Object toIndex () {         return  "forward:/home/index.html" ;     } } 
 
Service层 
LoginService
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 import  com.cbq.springsecuritystudy6.entity.User;import  com.cbq.springsecuritystudy6.filter.JwtCheckFilter;import  com.cbq.springsecuritystudy6.mapper.UserMapper;import  com.cbq.springsecuritystudy6.service.LoginService;import  com.cbq.springsecuritystudy6.service.RoleService;import  com.cbq.springsecuritystudy6.utils.JWTUtils;import  com.cbq.springsecuritystudy6.vo.SecurityUser;import  com.fasterxml.jackson.databind.ObjectMapper;import  org.springframework.security.core.authority.SimpleGrantedAuthority;import  org.springframework.stereotype.Service;import  javax.annotation.Resource;import  java.util.*;@Service public  class  LoginServiceImpl  implements  LoginService  {    @Resource      UserMapper userMapper;     @Resource      RoleService roleService;     @Resource      ObjectMapper objectMapper;     @Override      public  Object toLogin (String uid, String password)  {         Map<String, Object> result = new  HashMap <>();                  List<String> strings = roleService.queryPermissionsByUserUid(uid);         SecurityUser  securityUser  =  new  SecurityUser (User.builder().build());         List<SimpleGrantedAuthority> authorityList = new  ArrayList <>();         strings.forEach((v)->{authorityList.add(new  SimpleGrantedAuthority (v));});         securityUser.setAuthorityList(authorityList);                  JwtCheckFilter.redis.put("login:" +uid,securityUser);                  Map<String, String> tokenMap = new  HashMap <>();         tokenMap.put("uid" , uid);         String  token  =  JWTUtils.createToken(tokenMap);         result.put("code" , 200 );         result.put("msg" , "登录成功" );         result.put("token" , token);         return  result;     } } 
 
UserService
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 import  com.cbq.springsecuritystudy6.entity.User;import  com.cbq.springsecuritystudy6.mapper.UserMapper;import  com.cbq.springsecuritystudy6.service.UserService;import  org.springframework.stereotype.Service;import  javax.annotation.Resource;@Service public  class  UserServiceImpl  implements  UserService  {    @Resource      UserMapper userMapper;     @Override      public  User getUserFromUid (String uid)  {         User  user  =  userMapper.getUserByUid(uid);         return  user;     }     @Override      public  User userLogin (String uid, String password)  {         User  user  =  userMapper.getUser(uid, password);         return  user;     } } 
 
RoleService
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import  com.cbq.springsecuritystudy6.mapper.RoleMapper;import  com.cbq.springsecuritystudy6.service.RoleService;import  org.springframework.stereotype.Service;import  javax.annotation.Resource;import  java.util.List;@Service public  class  RoleServiceImpl  implements  RoleService  {    @Resource      private  RoleMapper roleMapper;     @Override      public  List<String> queryPermissionsByUserUid (String uid)  {         List<String> strings = roleMapper.queryRolePermissionsByUserId(uid);         return  strings;     } } 
 
Mapper 
UserMapper
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import  org.apache.ibatis.annotations.Param;import  org.apache.ibatis.annotations.Select;import  java.util.List;public  interface  RoleMapper  {         @Select("select role_menu.code from user,role,role_menu where user.role_uid=role.uid and role.uid=role_menu.role_uid AND user.uid=#{uid}")      public  List<String> queryRolePermissionsByUserId (@Param("uid")  String uid) ; } 
 
RoleMapper
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import  com.cbq.springsecuritystudy6.entity.User;import  org.apache.ibatis.annotations.Param;import  org.apache.ibatis.annotations.Select;public  interface  UserMapper  {         @Select("select * from user where uid=#{uid} and password=#{password}")      public  User getUser (@Param("uid") String uid,@Param("password") String password) ;          @Select("select * from user where uid=#{uid}")      public  User getUserByUid (@Param("uid") String uid) ; }