当前位置:   article > 正文

SpringBoot2.6 + SpringSecurity 分离版_securityconfig

securityconfig

目录

一:初始化项目

二:简单原理介绍

三:认证(UsernamePasswordAuthenticationFiter)

四:授权(FilterSecurityInterceptor)

五:失败处理(ExceptionTranslationFilter)

六:跨域

七:代码

1.POM文件

2.application.yml

3.config 配置文件(10个)

3.1.授权失败

3.2.认证失败

3.3.Springboot 跨域配置类

3.4.Redis使用FastJson序列化

3.5.token认证过滤器

3.6.Jwt工具类

3.7.redis工具

3.8.redis配置类

3.9.SpringSecurity 核心配置类

3.10.WebUtils

4.测试类

5.Login类

5.1.controller(1个)

5.2.dao(2个)

5.3.entity(4个)

5.4.mapper包(1个)

5.5.service包(2个接口,3个实现)

6.启动类

八:配置swagger

1.Swagger文档地址: http://localhost:8080/doc.html

2.yml文件配置

3.SecurityConfig 配置类

4.pom文件

5.swagger配置文件

九:SQL资料 参考


一:初始化项目

1、创建SpringBoot项目 

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

2、整合MyBatis

  1. <!-- mybatis -->
  2. <dependency>
  3. <groupId>org.mybatis.spring.boot</groupId>
  4. <artifactId>mybatis-spring-boot-starter</artifactId>
  5. <version>1.3.2</version>
  6. </dependency>
  7. <!-- mysql -->
  8. <dependency>
  9. <groupId>mysql</groupId>
  10. <artifactId>mysql-connector-java</artifactId>
  11. <scope>runtime</scope>
  12. </dependency>

3、引入SpringSecurity

  1. <!--spring security 启动器-->
  2. <dependency>
  3. <groupId>org.springframework.boot</groupId>
  4. <artifactId>spring-boot-starter-security</artifactId>
  5. </dependency>
  6. <!--jwt-->
  7. <dependency>
  8. <groupId>io.jsonwebtoken</groupId>
  9. <artifactId>jjwt</artifactId>
  10. <version>0.9.0</version>
  11. </dependency>

4、创建测试接口http://localhost:8080/hello,进行测试。
当引入security成功之后,在登录测试接口时就会跳转到默认的登录页http://localhost:8080/login。
(默认用户名:user,密码:控制台输出)

二:简单原理介绍

1、登录校验流程

 2、SpringSecurity简单过滤器链(完整的过滤器大概是14个),前后端分离一般用jwt,前后端不分离一般采用session

三:认证(UsernamePasswordAuthenticationFiter)

1、登录流程:

 ①、登录、自定义登录接口:
调用ProviderManager的方法进行认证,如果认证通过生成jwt和把用户信息存到redis中。

  1. // 认证 实现代码类
  2. UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(sysUser.getUserName(), sysUser.getPassword());
  3. Authentication authenticate = authenticationManager.authenticate(authenticationToken);
  4. // 认证通过 生成jwt
  5. LoginUser loginUser = (LoginUser) authenticate.getPrincipal();
  6. String userId = loginUser.getSysUser().getId().toString();
  7. String jwt = JwtUtil.createJWT(userId);// 使用userId生成 jwt
  8. Map<String, String> map = new HashMap<>(1);
  9. map.put("token", jwt);
  10. // 认证通过 存入 redis
  11. redisCache.setCacheObject("login:" + userId, loginUser);
  12. public class SecurityConfig extends WebSecurityConfigurerAdapter {
  13. //认证 配置代码
  14. @Override
  15. protected void configure(HttpSecurity http) throws Exception {
  16. // ......
  17. }
  18. }

②、登录、自定义UserDetailsService:
实现到db中查询用户信息,因为原接口是从内存中查询的。

  1. //重写 UserDetailsService 的 loadUserByUsername 方法
  2. public class UserDetailsServiceImpl implements UserDetailsService{
  3. @Override
  4. public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
  5. //......
  6. }
  7. }
  8. //重写UserDetails返回的用户信息
  9. public class LoginUser implements UserDetails {
  10. //......
  11. }

 2、token认证:

 ①、校验、定义jwt认证过滤器:
获取toekn、解析token获取其中的userId、从redis中获取用户信息、使用SecurityContextHolder.getContext().setAuthentication()方法存储该对象,这样其他过滤器会通过SecurityContextHolder来获取当前用户信息。

  1. // token认证过滤器
  2. @Component
  3. public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
  4. @Override
  5. protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
  6. // 省略...... 拦截到 token不合法等情况
  7. // 将 Authentication对象存入 SecurityContextHolder
  8. UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
  9. SecurityContextHolder.getContext().setAuthentication(authenticationToken);
  10. //放行
  11. filterChain.doFilter(request, response);
  12. }
  13. }

四:授权(FilterSecurityInterceptor)

1、将用户的权限信息封装到Authentcation当中,存到SecurityContextHolder中。

  1. @Service
  2. public class UserDetailsServiceImpl implements UserDetailsService {
  3. @Override
  4. public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
  5. // 省略 ......
  6. // 根据用户id查询权限字符串集合
  7. List<String> list = sysMenuDao.selectPermsByUserId(userInfo.getId());
  8. return new LoginUser(userInfo, list);
  9. }
  10. }
  11. public class LoginUser implements UserDetails {
  12. // ......
  13. @JSONField(serialize = false) //fastjson注解,表示此属性不会被序列化到redis当中
  14. private List<SimpleGrantedAuthority> authorities;
  15. @Override
  16. public Collection<? extends GrantedAuthority> getAuthorities() {
  17. // 权限为空的时候才往遍历,不为空直接返回
  18. if (authorities != null) {
  19. return authorities;
  20. }
  21. //把permissions中String类型的权限信息封装成SimpleGrantedAuthority对象
  22. authorities = new ArrayList<>();
  23. for (String permission : permissions) {
  24. SimpleGrantedAuthority authority = new SimpleGrantedAuthority(permission);
  25. authorities.add(authority);
  26. }
  27. return authorities;
  28. }
  29. // ......
  30. }
  31. // token过滤器中( JwtAuthenticationTokenFilter )
  32. // 将Authentication对象(用户信息、已认证状态、权限信息)存入 SecurityContextHolder
  33. UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
  34. SecurityContextHolder.getContext().setAuthentication(authenticationToken);

2、从SecurityContextHolder中获取Authentcation对象,再获取其中的权限信息。

  1. // token过滤器中( JwtAuthenticationTokenFilter )
  2. String redisKey = "login:" + userId;
  3. LoginUser loginUser = redisCache.getCacheObject(redisKey);// 从redis中获取用户信息

3、设置每个资源(方法)所需要的权限(注解形式设置权限)
RBAC(基于角色的权限控制)

  1. //SecurityConfig 配置类开启权限注解功能
  2. @EnableGlobalMethodSecurity(prePostEnabled = true)
  3. /***** 对应资源(方法)上授权 *****/
  4. //常用,该用户在数据库中有 system:dept:list 这个权限标识才能访问
  5. @PreAuthorize("hasAuthority('system:dept:list')")
  6. @PreAuthorize("hasAnyAuthority()")
  7. @PreAuthorize("hasAnyRole()")
  8. @PreAuthorize("hasAnyRole()")
  9. @PreAuthorize("hasPermission()")

五:失败处理(ExceptionTranslationFilter)

  1. public class SecurityConfig extends WebSecurityConfigurerAdapter {
  2. // ......
  3. @Override
  4. protected void configure(HttpSecurity http) throws Exception {
  5. http.exceptionHandling() // 配置异常处理器
  6. .authenticationEntryPoint(authenticationEntryPoint)// 认证失败
  7. .accessDeniedHandler(accessDeniedHandler); // 授权失败
  8. }
  9. // ......
  10. }

1、认证失败处理器

  1. @Component
  2. public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint {
  3. // ......
  4. }

2、授权失败处理器

  1. @Component
  2. public class AccessDeniedHandlerImpl implements AccessDeniedHandler {
  3. // ......
  4. }

六:跨域

同时处理 springboot跨域 和 springsecurity跨域

  1. // springboot 跨域配置类
  2. @Configuration
  3. public class CorsConfig implements WebMvcConfigurer {
  4. // ......
  5. }
  6. // SecurityConfig配置类
  7. http.cors();// spring security 允许跨域

七:代码

1.POM文件

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  4. <modelVersion>4.0.0</modelVersion>
  5. <parent>
  6. <groupId>org.springframework.boot</groupId>
  7. <artifactId>spring-boot-starter-parent</artifactId>
  8. <version>2.6.6</version>
  9. <relativePath/> <!-- lookup parent from repository -->
  10. </parent>
  11. <groupId>com.example</groupId>
  12. <artifactId>boot_security</artifactId>
  13. <version>0.0.1-SNAPSHOT</version>
  14. <name>boot_security</name>
  15. <description>Demo project for Spring Boot</description>
  16. <properties>
  17. <java.version>1.8</java.version>
  18. </properties>
  19. <dependencies>
  20. <!--spring boot-->
  21. <dependency>
  22. <groupId>org.springframework.boot</groupId>
  23. <artifactId>spring-boot-starter-web</artifactId>
  24. </dependency>
  25. <!--lombok-->
  26. <dependency>
  27. <groupId>org.projectlombok</groupId>
  28. <artifactId>lombok</artifactId>
  29. <optional>true</optional>
  30. </dependency>
  31. <!--spring security 启动器-->
  32. <dependency>
  33. <groupId>org.springframework.boot</groupId>
  34. <artifactId>spring-boot-starter-security</artifactId>
  35. </dependency>
  36. <!--redis-->
  37. <dependency>
  38. <groupId>org.springframework.boot</groupId>
  39. <artifactId>spring-boot-starter-data-redis</artifactId>
  40. </dependency>
  41. <!--fastjson-->
  42. <dependency>
  43. <groupId>com.alibaba</groupId>
  44. <artifactId>fastjson</artifactId>
  45. <version>1.2.33</version>
  46. </dependency>
  47. <!--jwt-->
  48. <dependency>
  49. <groupId>io.jsonwebtoken</groupId>
  50. <artifactId>jjwt</artifactId>
  51. <version>0.9.0</version>
  52. </dependency>
  53. <!-- mybatis -->
  54. <dependency>
  55. <groupId>org.mybatis.spring.boot</groupId>
  56. <artifactId>mybatis-spring-boot-starter</artifactId>
  57. <version>1.3.2</version>
  58. </dependency>
  59. <!-- mysql -->
  60. <dependency>
  61. <groupId>mysql</groupId>
  62. <artifactId>mysql-connector-java</artifactId>
  63. <scope>runtime</scope>
  64. </dependency>
  65. </dependencies>
  66. <build>
  67. <!--mybatis加载配置文件-->
  68. <resources>
  69. <resource>
  70. <directory>src/main/resources</directory>
  71. </resource>
  72. <resource>
  73. <directory>src/main/java</directory>
  74. <includes>
  75. <include>**/*.xml</include>
  76. </includes>
  77. </resource>
  78. </resources>
  79. <plugins>
  80. <plugin>
  81. <groupId>org.springframework.boot</groupId>
  82. <artifactId>spring-boot-maven-plugin</artifactId>
  83. </plugin>
  84. </plugins>
  85. </build>
  86. </project>

2.application.yml

  1. server:
  2. port: 8080
  3. spring:
  4. # 数据源配置
  5. datasource:
  6. url: jdbc:mysql://localhost:3306/security?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true
  7. username: root
  8. password: root
  9. driver-class-name: com.mysql.cj.jdbc.Driver
  10. # redis配置
  11. redis:
  12. host: localhost
  13. port: 6379
  14. password: 123456
  15. # mybatis配置
  16. mybatis:
  17. # 配置SQL映射文件路径
  18. # mapper-locations: classpath:/mapper/*.xml
  19. mapper-locations: classpath*:com/example/boot_security/**/mapper/*.xml
  20. # 驼峰命名
  21. configuration:
  22. mapUnderscoreToCamelCase: true

3.config 配置文件(10个)

3.1.授权失败

  1. /**
  2. * 授权失败
  3. */
  4. @Component
  5. public class AccessDeniedHandlerImpl implements AccessDeniedHandler {
  6. @Override
  7. public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
  8. ResponseResult result = new ResponseResult(403, "您的权限不足!");
  9. String json = JSON.toJSONString(result);
  10. // 将字符串渲染到客户端
  11. WebUtils.renderString(response, json);
  12. }
  13. }

3.2.认证失败

  1. /**
  2. * 认证失败
  3. */
  4. @Component
  5. public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint {
  6. @Override
  7. public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
  8. ResponseResult result = new ResponseResult(401, "认证失败,请重新登录!");
  9. String json = JSON.toJSONString(result);
  10. // 将字符串渲染到客户端
  11. WebUtils.renderString(response, json);
  12. }
  13. }

3.3.Springboot 跨域配置类

  1. /**
  2. * springboot 跨域配置类
  3. */
  4. @Configuration
  5. public class CorsConfig implements WebMvcConfigurer {
  6. @Override
  7. public void addCorsMappings(CorsRegistry registry) {
  8. // 设置允许跨域的路径
  9. registry.addMapping("/**")
  10. // 设置允许跨域请求的域名
  11. .allowedOriginPatterns("*")
  12. // 是否允许cookie
  13. .allowCredentials(true)
  14. // 设置允许的请求方式
  15. .allowedMethods("GET", "POST", "DELETE", "PUT")
  16. // 设置允许的header属性
  17. .allowedHeaders("*")
  18. // 跨域允许时间
  19. .maxAge(3600);
  20. }
  21. }

3.4.Redis使用FastJson序列化

  1. /**
  2. * Redis使用FastJson序列化
  3. */
  4. public class FastJsonRedisSerializer<T> implements RedisSerializer<T> {
  5. public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
  6. private Class<T> clazz;
  7. static {
  8. ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
  9. }
  10. public FastJsonRedisSerializer(Class<T> clazz) {
  11. super();
  12. this.clazz = clazz;
  13. }
  14. @Override
  15. public byte[] serialize(T t) throws SerializationException {
  16. if (t == null) {
  17. return new byte[0];
  18. }
  19. return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET);
  20. }
  21. @Override
  22. public T deserialize(byte[] bytes) throws SerializationException {
  23. if (bytes == null || bytes.length <= 0) {
  24. return null;
  25. }
  26. String str = new String(bytes, DEFAULT_CHARSET);
  27. return JSON.parseObject(str, clazz);
  28. }
  29. protected JavaType getJavaType(Class<?> clazz) {
  30. return TypeFactory.defaultInstance().constructType(clazz);
  31. }
  32. }

3.5.token认证过滤器

  1. /**
  2. * token认证过滤器
  3. * 作用:解析请求头中的token。并验证合法性
  4. * 继承 OncePerRequestFilter 保证请求经过过滤器一次
  5. */
  6. @Component
  7. public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
  8. @Resource
  9. private RedisCache redisCache;
  10. @Override
  11. protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
  12. String token = request.getHeader("token");
  13. // 没有token
  14. if (!StringUtils.hasText(token)) {
  15. filterChain.doFilter(request, response);//放行,因为后面的会抛出相应的异常
  16. return;
  17. }
  18. // 非法token
  19. String userId;
  20. try {
  21. Claims claims = JwtUtil.parseJWT(token);
  22. userId = claims.getSubject();
  23. } catch (Exception e) {
  24. e.printStackTrace();
  25. throw new RuntimeException("非法token!");
  26. }
  27. String redisKey = "login:" + userId;
  28. LoginUser loginUser = redisCache.getCacheObject(redisKey);// 从redis中获取用户信息
  29. // redis中用户不存在
  30. if (Objects.isNull(loginUser)) {
  31. throw new RuntimeException("redis中用户不存在!");
  32. }
  33. // 将Authentication对象(用户信息、已认证状态、权限信息)存入 SecurityContextHolder
  34. UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
  35. SecurityContextHolder.getContext().setAuthentication(authenticationToken);
  36. //放行
  37. filterChain.doFilter(request, response);
  38. }
  39. }

3.6.Jwt工具类

  1. /**
  2. * Jwt工具类
  3. */
  4. public class JwtUtil {
  5. /**
  6. * 有效期为
  7. * 60 * 60 *1000 一个小时
  8. */
  9. public static final Long JWT_TTL = 60 * 60 * 1000L;
  10. /**
  11. * 设置秘钥明文
  12. */
  13. public static final String JWT_KEY = "sangeng";
  14. public static String getUUID() {
  15. String token = UUID.randomUUID().toString().replaceAll("-", "");
  16. return token;
  17. }
  18. /**
  19. * 生成 jtw
  20. *
  21. * @param subject token中要存放的数据(json格式)
  22. * @return
  23. */
  24. public static String createJWT(String subject) {
  25. // 设置过期时间 空
  26. JwtBuilder builder = getJwtBuilder(subject, null, getUUID());
  27. return builder.compact();
  28. }
  29. /**
  30. * 生成 jtw
  31. *
  32. * @param subject token中要存放的数据(json格式)
  33. * @param ttlMillis token超时时间
  34. * @return
  35. */
  36. public static String createJWT(String subject, Long ttlMillis) {
  37. // 设置过期时间
  38. JwtBuilder builder = getJwtBuilder(subject, ttlMillis, getUUID());
  39. return builder.compact();
  40. }
  41. private static JwtBuilder getJwtBuilder(String subject, Long ttlMillis, String uuid) {
  42. SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
  43. SecretKey secretKey = generalKey();
  44. long nowMillis = System.currentTimeMillis();
  45. Date now = new Date(nowMillis);
  46. if (ttlMillis == null) {
  47. ttlMillis = JwtUtil.JWT_TTL;
  48. }
  49. long expMillis = nowMillis + ttlMillis;
  50. Date expDate = new Date(expMillis);
  51. return Jwts.builder()
  52. // 唯一的ID
  53. .setId(uuid)
  54. // 主题 可以是JSON数据
  55. .setSubject(subject)
  56. // 签发者
  57. .setIssuer("sg")
  58. // 签发时间
  59. .setIssuedAt(now)
  60. // 使用 HS256 对称加密算法签名, 第二个参数为秘钥
  61. .signWith(signatureAlgorithm, secretKey)
  62. .setExpiration(expDate);
  63. }
  64. /**
  65. * 创建 token
  66. *
  67. * @param id
  68. * @param subject
  69. * @param ttlMillis
  70. * @return
  71. */
  72. public static String createJWT(String id, String subject, Long ttlMillis) {
  73. // 设置过期时间
  74. JwtBuilder builder = getJwtBuilder(subject, ttlMillis, id);
  75. return builder.compact();
  76. }
  77. /**
  78. * 生成加密后的秘钥 secretKey
  79. *
  80. * @return
  81. */
  82. public static SecretKey generalKey() {
  83. byte[] encodedKey = Base64.getDecoder().decode(JwtUtil.JWT_KEY);
  84. SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
  85. return key;
  86. }
  87. /**
  88. * 解析
  89. *
  90. * @param jwt
  91. * @return
  92. * @throws Exception
  93. */
  94. public static Claims parseJWT(String jwt) throws Exception {
  95. SecretKey secretKey = generalKey();
  96. return Jwts.parser()
  97. .setSigningKey(secretKey)
  98. .parseClaimsJws(jwt)
  99. .getBody();
  100. }
  101. //测试方法
  102. public static void main(String[] args) throws Exception {
  103. //JWT加密
  104. String jwt = createJWT("123456");
  105. System.out.println(jwt);
  106. //JWT解密 时间过期会报错,须重新生成再解析
  107. Claims claims = parseJWT("密文");
  108. String subject = claims.getSubject();
  109. System.out.println(subject);
  110. }
  111. }

3.7.redis工具

  1. /**
  2. * redis工具类
  3. */
  4. @SuppressWarnings(value = {"unchecked", "rawtypes"})
  5. @Component
  6. public class RedisCache {
  7. @Autowired
  8. public RedisTemplate redisTemplate;
  9. /**
  10. * 缓存基本的对象,Integer、String、实体类等
  11. *
  12. * @param key 缓存的键值
  13. * @param value 缓存的值
  14. */
  15. public <T> void setCacheObject(final String key, final T value) {
  16. redisTemplate.opsForValue().set(key, value);
  17. }
  18. /**
  19. * 缓存基本的对象,Integer、String、实体类等
  20. *
  21. * @param key 缓存的键值
  22. * @param value 缓存的值
  23. * @param timeout 时间
  24. * @param timeUnit 时间颗粒度
  25. */
  26. public <T> void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit) {
  27. redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
  28. }
  29. /**
  30. * 设置有效时间
  31. *
  32. * @param key Redis键
  33. * @param timeout 超时时间
  34. * @return true=设置成功;false=设置失败
  35. */
  36. public boolean expire(final String key, final long timeout) {
  37. return expire(key, timeout, TimeUnit.SECONDS);
  38. }
  39. /**
  40. * 设置有效时间
  41. *
  42. * @param key Redis键
  43. * @param timeout 超时时间
  44. * @param unit 时间单位
  45. * @return true=设置成功;false=设置失败
  46. */
  47. public boolean expire(final String key, final long timeout, final TimeUnit unit) {
  48. return redisTemplate.expire(key, timeout, unit);
  49. }
  50. /**
  51. * 获得缓存的基本对象。
  52. *
  53. * @param key 缓存键值
  54. * @return 缓存键值对应的数据
  55. */
  56. public <T> T getCacheObject(final String key) {
  57. ValueOperations<String, T> operation = redisTemplate.opsForValue();
  58. return operation.get(key);
  59. }
  60. /**
  61. * 删除单个对象
  62. *
  63. * @param key
  64. */
  65. public boolean deleteObject(final String key) {
  66. return redisTemplate.delete(key);
  67. }
  68. /**
  69. * 删除集合对象
  70. *
  71. * @param collection 多个对象
  72. * @return
  73. */
  74. public long deleteObject(final Collection collection) {
  75. return redisTemplate.delete(collection);
  76. }
  77. /**
  78. * 缓存List数据
  79. *
  80. * @param key 缓存的键值
  81. * @param dataList 待缓存的List数据
  82. * @return 缓存的对象
  83. */
  84. public <T> long setCacheList(final String key, final List<T> dataList) {
  85. Long count = redisTemplate.opsForList().rightPushAll(key, dataList);
  86. return count == null ? 0 : count;
  87. }
  88. /**
  89. * 获得缓存的list对象
  90. *
  91. * @param key 缓存的键值
  92. * @return 缓存键值对应的数据
  93. */
  94. public <T> List<T> getCacheList(final String key) {
  95. return redisTemplate.opsForList().range(key, 0, -1);
  96. }
  97. /**
  98. * 缓存Set
  99. *
  100. * @param key 缓存键值
  101. * @param dataSet 缓存的数据
  102. * @return 缓存数据的对象
  103. */
  104. public <T> BoundSetOperations<String, T> setCacheSet(final String key, final Set<T> dataSet) {
  105. BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps(key);
  106. Iterator<T> it = dataSet.iterator();
  107. while (it.hasNext()) {
  108. setOperation.add(it.next());
  109. }
  110. return setOperation;
  111. }
  112. /**
  113. * 获得缓存的set
  114. *
  115. * @param key
  116. * @return
  117. */
  118. public <T> Set<T> getCacheSet(final String key) {
  119. return redisTemplate.opsForSet().members(key);
  120. }
  121. /**
  122. * 缓存Map
  123. *
  124. * @param key
  125. * @param dataMap
  126. */
  127. public <T> void setCacheMap(final String key, final Map<String, T> dataMap) {
  128. if (dataMap != null) {
  129. redisTemplate.opsForHash().putAll(key, dataMap);
  130. }
  131. }
  132. /**
  133. * 获得缓存的Map
  134. *
  135. * @param key
  136. * @return
  137. */
  138. public <T> Map<String, T> getCacheMap(final String key) {
  139. return redisTemplate.opsForHash().entries(key);
  140. }
  141. /**
  142. * 往Hash中存入数据
  143. *
  144. * @param key Redis键
  145. * @param hKey Hash键
  146. * @param value 值
  147. */
  148. public <T> void setCacheMapValue(final String key, final String hKey, final T value) {
  149. redisTemplate.opsForHash().put(key, hKey, value);
  150. }
  151. /**
  152. * 获取Hash中的数据
  153. *
  154. * @param key Redis键
  155. * @param hKey Hash键
  156. * @return Hash中的对象
  157. */
  158. public <T> T getCacheMapValue(final String key, final String hKey) {
  159. HashOperations<String, String, T> opsForHash = redisTemplate.opsForHash();
  160. return opsForHash.get(key, hKey);
  161. }
  162. /**
  163. * 删除Hash中的数据
  164. *
  165. * @param key
  166. * @param hkey
  167. */
  168. public void delCacheMapValue(final String key, final String hkey) {
  169. HashOperations hashOperations = redisTemplate.opsForHash();
  170. hashOperations.delete(key, hkey);
  171. }
  172. /**
  173. * 获取多个Hash中的数据
  174. *
  175. * @param key Redis键
  176. * @param hKeys Hash键集合
  177. * @return Hash对象集合
  178. */
  179. public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys) {
  180. return redisTemplate.opsForHash().multiGet(key, hKeys);
  181. }
  182. /**
  183. * 获得缓存的基本对象列表
  184. *
  185. * @param pattern 字符串前缀
  186. * @return 对象列表
  187. */
  188. public Collection<String> keys(final String pattern) {
  189. return redisTemplate.keys(pattern);
  190. }
  191. }

3.8.redis配置类

  1. /**
  2. * redis配置类
  3. * 避免存入redis中的key看上去乱码的现象
  4. */
  5. @Configuration
  6. public class RedisConfig {
  7. @Bean
  8. @SuppressWarnings(value = {"unchecked", "rawtypes"})
  9. public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
  10. RedisTemplate<Object, Object> template = new RedisTemplate<>();
  11. template.setConnectionFactory(connectionFactory);
  12. FastJsonRedisSerializer serializer = new FastJsonRedisSerializer(Object.class);
  13. // 使用StringRedisSerializer来序列化和反序列化redis的key值
  14. template.setKeySerializer(new StringRedisSerializer());
  15. template.setValueSerializer(serializer);
  16. // Hash的key也采用StringRedisSerializer的序列化方式
  17. template.setHashKeySerializer(new StringRedisSerializer());
  18. template.setHashValueSerializer(serializer);
  19. template.afterPropertiesSet();
  20. return template;
  21. }
  22. }

3.9.SpringSecurity 核心配置类

  1. /**
  2. * SpringSecurity 核心配置类
  3. * prePostEnabled = true 开启注解权限认证功能
  4. */
  5. @Configuration
  6. @EnableGlobalMethodSecurity(prePostEnabled = true)//开启@PreAuthorize()注解权限功能
  7. public class SecurityConfig extends WebSecurityConfigurerAdapter {
  8. //认证过滤器
  9. @Autowired
  10. private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
  11. // 认证失败处理器
  12. @Autowired
  13. private AuthenticationEntryPointImpl authenticationEntryPoint;
  14. // 授权失败处理器
  15. @Autowired
  16. private AccessDeniedHandlerImpl accessDeniedHandler;
  17. /**
  18. * 密码机密处理器
  19. * <p>
  20. * 将BCryptPasswordEncoder对象注入到spring容器中,更换掉原来的 PasswordEncoder加密方式
  21. * 原PasswordEncoder密码格式为:{id}password。它会根据id去判断密码的加密方式。
  22. * 如果没替换原来的加密方式,数据库中想用明文密码做测试,将密码字段改为{noop}123456这样的格式
  23. */
  24. @Bean
  25. public PasswordEncoder passwordEncoder() {
  26. return new BCryptPasswordEncoder();
  27. }
  28. /**
  29. * 认证配置
  30. * anonymous():匿名访问:未登录可以访问,已登录不能访问
  31. * permitAll():有没有认证都能访问:登录或未登录都能访问
  32. * denyAll(): 拒绝
  33. * authenticated():认证之后才能访问
  34. * hasAuthority():包含权限
  35. */
  36. @Override
  37. protected void configure(HttpSecurity http) throws Exception {
  38. http
  39. // 关闭csrf(前后端分离项目要关闭此功能)
  40. .csrf().disable()
  41. // 禁用session (前后端分离项目,不通过Session获取SecurityContext)
  42. .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
  43. .and()
  44. // 请求认证配置
  45. .authorizeRequests()
  46. // 允许匿名访问:未登录可以访问,已登录不能访问
  47. .antMatchers("/login").anonymous()
  48. // .antMatchers("/login").permitAll()// 登录或未登录都能访问
  49. // .antMatchers("/textMybatis").hasAuthority("system:dept:list22")
  50. // 任意用户,认证之后才可以访问(除上面外的)
  51. .anyRequest().authenticated();
  52. // 添加token过滤器
  53. http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
  54. // 配置异常处理器
  55. http.exceptionHandling()
  56. // 认证失败
  57. .authenticationEntryPoint(authenticationEntryPoint)
  58. // 授权失败
  59. .accessDeniedHandler(accessDeniedHandler);
  60. // spring security 允许跨域
  61. http.cors();
  62. }
  63. /**
  64. * 注入AuthenticationManager 进行用户认证
  65. */
  66. @Bean
  67. @Override
  68. public AuthenticationManager authenticationManagerBean() throws Exception {
  69. return super.authenticationManagerBean();
  70. }
  71. }

3.10.WebUtils

  1. /**
  2. * 将字符串渲染到客户端
  3. * 向响应之中写入中聚类
  4. *
  5. * @author bing_ @create 2022/1/4-22:20
  6. */
  7. public class WebUtils {
  8. /**
  9. * 将字符串渲染到客户端
  10. *
  11. * @param response 渲染对象
  12. * @param string 待渲染的字符串
  13. * @return null
  14. */
  15. public static String renderString(HttpServletResponse response, String string) {
  16. try {
  17. response.setStatus(200);
  18. response.setContentType("application/json");
  19. response.setCharacterEncoding("utf-8");
  20. response.getWriter().print(string);
  21. } catch (IOException e) {
  22. e.printStackTrace();
  23. }
  24. return null;
  25. }
  26. }

4.测试类

  1. /**
  2. * 测试接口 账户名:sg 密码:1111
  3. */
  4. @RestController
  5. @RequestMapping("/test")
  6. public class HelloController {
  7. @Autowired
  8. private SysUserDao sysUserDao;
  9. /**
  10. * 测试 springBoot
  11. */
  12. @GetMapping("/SpringBoot")
  13. @PreAuthorize("hasAuthority('system:dept:list')")//授权
  14. public String testSpringBoot(){
  15. return "测试 springBoot";
  16. }
  17. /**
  18. * 测试 mybatis
  19. */
  20. @GetMapping("/Mybatis")
  21. public SysUser textMybatis(){
  22. return sysUserDao.getUserInfo("sg");
  23. }
  24. }

5.Login类

5.1.controller(1个)

1.LoginController 

  1. /**
  2. * 一个坑:退出路径如果只有 "/logout" 会报403 ,原因不明
  3. * 解决方法:前面添加个前路径,或者换一个名就可以了
  4. */
  5. @RestController
  6. public class LoginController {
  7. @Autowired
  8. private LoginService loginService;
  9. /**
  10. * 登录
  11. */
  12. @PostMapping("/login")
  13. public ResponseResult login(@RequestBody SysUser sysUser) {
  14. return loginService.login(sysUser);
  15. }
  16. /**
  17. * 退出登录
  18. */
  19. @GetMapping("/user/logout")
  20. public ResponseResult logout() {
  21. return loginService.logout();
  22. }
  23. }

5.2.dao(2个)

5.2.1.SysMenuDao 

  1. public interface SysMenuDao {
  2. /**
  3. * 根据用户id查询权限字符串集合
  4. */
  5. List<String> selectPermsByUserId(Long userId);
  6. }

5.2.2.SysUserDao  

  1. public interface SysUserDao {
  2. /**
  3. * 获取用户信息
  4. */
  5. @Select("select * from sys_user where user_name = #{username}")
  6. SysUser getUserInfo(String username);
  7. }

5.3.entity(4个)

5.3.1.SysUser 

  1. /**
  2. * 用户表(sys_user)实体表
  3. */
  4. @Data
  5. @AllArgsConstructor
  6. @NoArgsConstructor
  7. public class SysUser {
  8. private Long id;//主键
  9. private String userName;//用户名
  10. private String nickName;//昵称
  11. private String password;//密码
  12. private String status;//账号状态(0正常 1停用)
  13. private String email;//邮箱
  14. private String phonenumber;//手机号
  15. private String sex;//用户性别(0男,1女,2未知)
  16. private String avatar;//头像
  17. private String userType;//用户类型(0管理员,1普通用户)
  18. private Long createBy;//创建人的用户id
  19. private LocalDateTime createTime;//创建时间
  20. private Long updateBy;//更新人
  21. private LocalDateTime updateTime;//更新时间
  22. private Integer delFlag;//删除标志(0代表未删除,1代表已删除)
  23. }

5.3.2.LoginUser  

  1. /**
  2. * 重写UserDetails返回的用户信息
  3. * SpringSecurity返回的用户信息实体类
  4. */
  5. @Data
  6. @NoArgsConstructor
  7. public class LoginUser implements UserDetails {
  8. private SysUser sysUser;//用户信息
  9. private List<String> permissions;//权限信息
  10. public LoginUser(SysUser sysUser, List<String> permissions) {
  11. this.sysUser = sysUser;
  12. this.permissions = permissions;
  13. }
  14. @JSONField(serialize = false) //fastjson注解,表示此属性不会被序列化,因为SimpleGrantedAuthority这个类型不能在redis中序列化
  15. private List<SimpleGrantedAuthority> authorities;
  16. /**
  17. * 获取权限信息
  18. */
  19. @Override
  20. public Collection<? extends GrantedAuthority> getAuthorities() {
  21. // 权限为空的时候才往遍历,不为空直接返回
  22. if (authorities != null) {
  23. return authorities;
  24. }
  25. //把permissions中String类型的权限信息封装成SimpleGrantedAuthority对象
  26. authorities = new ArrayList<>();
  27. for (String permission : permissions) {
  28. SimpleGrantedAuthority authority = new SimpleGrantedAuthority(permission);
  29. authorities.add(authority);
  30. }
  31. return authorities;
  32. }
  33. /**
  34. * 获取密码
  35. */
  36. @Override
  37. public String getPassword() {
  38. return sysUser.getPassword();
  39. }
  40. /**
  41. * 获取用户名
  42. */
  43. @Override
  44. public String getUsername() {
  45. return sysUser.getUserName();
  46. }
  47. /**
  48. * 判断是否过期
  49. */
  50. @Override
  51. public boolean isAccountNonExpired() {
  52. return true;
  53. }
  54. /**
  55. * 是否锁定
  56. */
  57. @Override
  58. public boolean isAccountNonLocked() {
  59. return true;
  60. }
  61. /**
  62. * 是否没有超时
  63. */
  64. @Override
  65. public boolean isCredentialsNonExpired() {
  66. return true;
  67. }
  68. /**
  69. * 是否可用
  70. */
  71. @Override
  72. public boolean isEnabled() {
  73. return true;
  74. }
  75. }

5.3.3. SysMenu

  1. /**
  2. * 菜单表(sys_menu)实体表
  3. */
  4. @Data
  5. public class SysMenu{
  6. private Long id;
  7. private String menuName;//菜单名
  8. private String path;//路由地址
  9. private String component;//组件路径
  10. private String visible;//菜单状态(0显示 1隐藏)
  11. private String status;//菜单状态(0正常 1停用)
  12. private String perms;//权限标识
  13. private String icon;//菜单图标
  14. private Long createBy;
  15. private LocalDateTime createTime;
  16. private Long updateBy;
  17. private LocalDateTime updateTime;
  18. private Integer delFlag;//是否删除(0未删除 1已删除)
  19. private String remark;//备注
  20. }

5.3.4.SysUser 

  1. /**
  2. * 用户表(sys_user)实体表
  3. */
  4. @Data
  5. @AllArgsConstructor
  6. @NoArgsConstructor
  7. public class SysUser {
  8. private Long id;//主键
  9. private String userName;//用户名
  10. private String nickName;//昵称
  11. private String password;//密码
  12. private String status;//账号状态(0正常 1停用)
  13. private String email;//邮箱
  14. private String phonenumber;//手机号
  15. private String sex;//用户性别(0男,1女,2未知)
  16. private String avatar;//头像
  17. private String userType;//用户类型(0管理员,1普通用户)
  18. private Long createBy;//创建人的用户id
  19. private LocalDateTime createTime;//创建时间
  20. private Long updateBy;//更新人
  21. private LocalDateTime updateTime;//更新时间
  22. private Integer delFlag;//删除标志(0代表未删除,1代表已删除)
  23. }

5.4.mapper包(1个)

5.4.1.SysMenuMapper.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  3. <mapper namespace="com.example.boot_security.sysSecurity.dao.SysMenuDao">
  4. <!--根据用户id查询权限字符串集合-->
  5. <select id="selectPermsByUserId" resultType="java.lang.String">
  6. SELECT DISTINCT m.perms
  7. FROM sys_user_role ur
  8. LEFT JOIN sys_role r ON ur.role_id = r.id
  9. LEFT JOIN sys_role_menu rm ON r.id = rm.role_id
  10. LEFT JOIN sys_menu m ON rm.menu_id = m.id
  11. WHERE ur.user_id = #{userId}
  12. </select>
  13. </mapper>

5.5.service包(2个接口,3个实现)

5.5.1.LoginService 接口

  1. public interface LoginService {
  2. /**
  3. * 登录
  4. */
  5. ResponseResult login(SysUser sysUser);
  6. /**
  7. * 退出登录
  8. */
  9. ResponseResult logout();
  10. }

5.5.2.SysUserService 接口

  1. public interface SysUserService {
  2. /**
  3. * 根据用户名获取用户信息
  4. */
  5. SysUser getUserInfo(String username);
  6. }

5.5.3.impl实现类

5.5.3.1.LoginServiceImpl

  1. @Service
  2. public class LoginServiceImpl implements LoginService {
  3. @Autowired
  4. private AuthenticationManager authenticationManager;
  5. @Autowired
  6. private RedisCache redisCache;
  7. @Override
  8. public ResponseResult login(SysUser sysUser) {
  9. // 认证
  10. UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(sysUser.getUserName(), sysUser.getPassword());
  11. Authentication authenticate = authenticationManager.authenticate(authenticationToken);
  12. // 认证没通过
  13. if (Objects.isNull(authenticate)) {
  14. throw new RuntimeException("用户名或密码错误!");
  15. }
  16. // 认证通过 生成jwt
  17. LoginUser loginUser = (LoginUser) authenticate.getPrincipal();
  18. String userId = loginUser.getSysUser().getId().toString();
  19. String jwt = JwtUtil.createJWT(userId);
  20. Map<String, String> map = new HashMap<>(1);
  21. map.put("token", jwt);
  22. // 认证通过 存入 redis
  23. redisCache.setCacheObject("login:" + userId, loginUser);
  24. return new ResponseResult(200, "登录成功", map);
  25. }
  26. @Override
  27. public ResponseResult logout() {
  28. LoginUser loginUser = (LoginUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
  29. Long userId = loginUser.getSysUser().getId();
  30. // 清空redis
  31. redisCache.deleteObject("login:" + userId);
  32. return new ResponseResult(200, "退出成功");
  33. }
  34. }

5.5.3.2.SysUserServiceImpl

  1. @Service
  2. public class SysUserServiceImpl implements SysUserService {
  3. @Autowired
  4. private SysUserDao systemUserDao;
  5. /**
  6. * 根据用户名获取用户信息
  7. */
  8. @Override
  9. public SysUser getUserInfo(String username) {
  10. return systemUserDao.getUserInfo(username);
  11. }
  12. }

5.5.3.3.UserDetailsServiceImpl

  1. /**
  2. * 重写框架的 UserDetailsService 的 loadUserByUsername 方法
  3. * 从数据库中查询用户信息
  4. * 因为原框架的内用信息是存在内存中的
  5. */
  6. @Service
  7. public class UserDetailsServiceImpl implements UserDetailsService {
  8. @Autowired
  9. private SysUserService sysUserService;
  10. @Autowired
  11. private SysMenuDao sysMenuDao;
  12. @Override
  13. public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
  14. SysUser userInfo = sysUserService.getUserInfo(username);
  15. if (Objects.isNull(userInfo)) {
  16. throw new RuntimeException("用户名和密码错误!");
  17. }
  18. List<String> list = sysMenuDao.selectPermsByUserId(userInfo.getId());
  19. // 把数据封装成 UserDetails 返回,参数:用户信息、权限列表
  20. return new LoginUser(userInfo, list);
  21. }
  22. }

6.启动类

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

项目结构 

 

八:配置swagger

1.Swagger文档地址: http://localhost:8080/doc.html

2.yml文件配置

  1. # Spring 高版本Springboot+swagger3.0配置,不然报错
  2. spring:
  3. mvc:
  4. pathmatch:
  5. matching-strategy: ant_path_matcher

3.SecurityConfig 配置类

  1. @Override
  2. protected void configure(HttpSecurity http) throws Exception {
  3. http.authorizeHttpRequests()
  4. //对于登录login 注册register 验证码captchaImage 允许匿名访问
  5. .antMatchers("/login/**").permitAll()
  6. //静态资源,可匿名访问
  7. .antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll()
  8. .antMatchers("/swagger-ui.html", "/doc.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll()
  9. .anyRequest()
  10. //所有请求都必须认证
  11. .authenticated()
  12. .and()
  13. .formLogin()
  14. .and()
  15. .exceptionHandling()
  16. //没登录提示信息
  17. .authenticationEntryPoint((req, resp, ex) -> {
  18. resp.setContentType("application/json;charset=UTF-8");
  19. resp.setStatus(HttpStatus.UNAUTHORIZED.value());
  20. resp.getWriter().println("必须认证之后才能访问!");
  21. })
  22. //退出登录提示信息
  23. .and().logout().logoutUrl("/logout")
  24. .logoutSuccessHandler((req, resp, ex) -> {
  25. resp.setContentType("application/json;charset=UTF-8");
  26. resp.setStatus(HttpStatus.UNAUTHORIZED.value());
  27. resp.getWriter().println("注销成功!");
  28. })
  29. //跨域处理方案
  30. .and().cors().configurationSource(configurationSource())
  31. //关闭csrf
  32. .and().csrf().disable()
  33. ;

4.pom文件

  1. <!--swagger 高版本Springboot2.6+使用3.0以上-->
  2. <dependency>
  3. <groupId>io.springfox</groupId>
  4. <artifactId>springfox-swagger2</artifactId>
  5. <version>3.0.0</version>
  6. </dependency>
  7. <dependency>
  8. <groupId>io.springfox</groupId>
  9. <artifactId>springfox-boot-starter</artifactId>
  10. <version>3.0.0</version>
  11. </dependency>
  12. <!--swagger ui 高版本Springboot2.6+使用3.0以上-->
  13. <dependency>
  14. <groupId>com.github.xiaoymin</groupId>
  15. <artifactId>knife4j-spring-boot-starter</artifactId>
  16. <version>3.0.3</version>
  17. </dependency>

5.swagger配置文件

  1. import io.swagger.annotations.ApiOperation;
  2. import org.springframework.beans.BeansException;
  3. import org.springframework.beans.factory.config.BeanPostProcessor;
  4. import org.springframework.context.annotation.Bean;
  5. import org.springframework.context.annotation.Configuration;
  6. import org.springframework.util.ReflectionUtils;
  7. import org.springframework.web.bind.annotation.RestController;
  8. import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping;
  9. import springfox.documentation.builders.ApiInfoBuilder;
  10. import springfox.documentation.builders.ParameterBuilder;
  11. import springfox.documentation.builders.PathSelectors;
  12. import springfox.documentation.builders.RequestHandlerSelectors;
  13. import springfox.documentation.schema.ModelRef;
  14. import springfox.documentation.service.*;
  15. import springfox.documentation.spi.DocumentationType;
  16. import springfox.documentation.spi.service.contexts.SecurityContext;
  17. import springfox.documentation.spring.web.plugins.Docket;
  18. import springfox.documentation.spring.web.plugins.WebFluxRequestHandlerProvider;
  19. import springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider;
  20. import java.lang.reflect.Field;
  21. import java.util.ArrayList;
  22. import java.util.Collections;
  23. import java.util.List;
  24. import java.util.stream.Collectors;
  25. @Configuration
  26. public class SwaggerConfig {
  27. /**
  28. * swagger2的配置文件,这里可以配置swagger2的一些基本的内容,比如扫描的包等等
  29. *
  30. * @return Docket
  31. */
  32. @Bean(value = "defaultApi2")
  33. public Docket defaultApi2() {
  34. return new Docket(DocumentationType.SWAGGER_2)
  35. //是否启用Swagger
  36. .enable(true)
  37. .apiInfo(apiInfo())
  38. .select()
  39. //此包路径下的类,才生成接口文档
  40. //.apis(RequestHandlerSelectors.basePackage("com.cn"))
  41. //扫描所有
  42. //.apis(RequestHandlerSelectors.any())
  43. //加了ApiOperation注解的类,才生成接口文档
  44. .apis(RequestHandlerSelectors.withClassAnnotation(RestController.class))
  45. .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
  46. .paths(PathSelectors.any())
  47. .build()
  48. //设置自定义swagger请求头
  49. .securitySchemes(Collections.singletonList(securityScheme()))
  50. .securityContexts(securityContexts());
  51. //.globalOperationParameters(setHeaderToken());
  52. }
  53. /**
  54. * 设置自定义swagger请求头
  55. * @return ApiKey
  56. */
  57. @Bean
  58. SecurityScheme securityScheme() {
  59. return new ApiKey("Access-Token", "Access-Token", "header");
  60. }
  61. /**
  62. * 新增 securityContexts 保持登录状态
  63. */
  64. private List<SecurityContext> securityContexts() {
  65. return new ArrayList(
  66. Collections.singleton(SecurityContext.builder()
  67. .securityReferences(defaultAuth())
  68. .forPaths(PathSelectors.regex("^(?!auth).*$"))
  69. .build())
  70. );
  71. }
  72. /**
  73. * 配置
  74. */
  75. private List<SecurityReference> defaultAuth() {
  76. AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
  77. AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
  78. authorizationScopes[0] = authorizationScope;
  79. return new ArrayList(
  80. Collections.singleton(new SecurityReference("Access-Token", authorizationScopes)));
  81. }
  82. /**
  83. * swagger主页名
  84. * @return ApiInfo
  85. */
  86. private ApiInfo apiInfo() {
  87. return new ApiInfoBuilder()
  88. .title("接口文档")
  89. .description("swagger接口文档")
  90. .version("1.0")
  91. .build();
  92. }
  93. /**
  94. * JWT token
  95. * @return setHeaderToken
  96. */
  97. private List<Parameter> setHeaderToken() {
  98. ParameterBuilder tokenPar = new ParameterBuilder();
  99. List<Parameter> pars = new ArrayList<>();
  100. tokenPar.name("Access-Token").description("token").modelRef(new ModelRef("string")).parameterType("header").required(false).build();
  101. pars.add(tokenPar.build());
  102. return pars;
  103. }
  104. /**
  105. * 高版本Springboot2.6+务必加入配置类中的springfoxHandlerProviderBeanPostProcessor方法
  106. * @return BeanPostProcessor
  107. */
  108. @Bean
  109. public static BeanPostProcessor springfoxHandlerProviderBeanPostProcessor() {
  110. return new BeanPostProcessor() {
  111. @Override
  112. public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
  113. if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider) {
  114. customizeSpringfoxHandlerMappings(getHandlerMappings(bean));
  115. }
  116. return bean;
  117. }
  118. private <T extends RequestMappingInfoHandlerMapping> void customizeSpringfoxHandlerMappings(List<T> mappings) {
  119. List<T> copy = mappings.stream()
  120. .filter(mapping -> mapping.getPatternParser() == null)
  121. .collect(Collectors.toList());
  122. mappings.clear();
  123. mappings.addAll(copy);
  124. }
  125. @SuppressWarnings("unchecked")
  126. private List<RequestMappingInfoHandlerMapping> getHandlerMappings(Object bean) {
  127. try {
  128. Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings");
  129. field.setAccessible(true);
  130. return (List<RequestMappingInfoHandlerMapping>) field.get(bean);
  131. } catch (IllegalArgumentException | IllegalAccessException e) {
  132. throw new IllegalStateException(e);
  133. }
  134. }
  135. };
  136. }
  137. }

九:SQL资料 参考

由于RBAC模型主要包含用户、角色、权限。因此本文主要设计了五张表:sys_user(用户表)、sys_menu(菜单表)、sys_role(权限表)、sys_role_menu(角色权限关联表)、sys_user_role(用户角色关联表)。

  1. CREATE TABLE `sys_menu` (
  2. `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
  3. `menu_name` varchar(64) NOT NULL DEFAULT 'NULL' COMMENT '菜单名',
  4. `path` varchar(200) DEFAULT NULL COMMENT '路由地址',
  5. `component` varchar(255) DEFAULT NULL COMMENT '组件路径',
  6. `visible` char(1) DEFAULT '0' COMMENT '菜单状态(0显示 1隐藏)',
  7. `status` char(1) DEFAULT '0' COMMENT '菜单状态(0正常 1停用)',
  8. `perms` varchar(255) DEFAULT NULL COMMENT '权限标识',
  9. `icon` varchar(100) DEFAULT '#' COMMENT '菜单图标',
  10. `create_time` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
  11. `modify_time` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
  12. `del_flag` int(11) DEFAULT '0' COMMENT '是否删除(0未删除 1已删除)',
  13. `remark` varchar(500) DEFAULT NULL COMMENT '备注',
  14. PRIMARY KEY (`id`)
  15. )ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
  16. CREATE TABLE `sys_role` (
  17. `id` bigint(200) NOT NULL AUTO_INCREMENT COMMENT '主键',
  18. `name` varchar(64) NOT NULL DEFAULT 'NULL' COMMENT '角色名称',
  19. `role_key` varchar(100) DEFAULT NULL COMMENT '角色权限字符串',
  20. `status` char(1) DEFAULT '0' COMMENT '菜单状态(0正常 1停用)',
  21. `del_flag` int(11) DEFAULT '0' COMMENT '是否删除(0未删除 1已删除)',
  22. `create_time` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
  23. `modify_time` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
  24. `update_by` bigint(200) DEFAULT NULL,
  25. `remark` varchar(500) DEFAULT NULL COMMENT '备注',
  26. PRIMARY KEY (`id`)
  27. )ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
  28. CREATE TABLE `sys_role_menu` (
  29. `role_id` bigint(200) NOT NULL AUTO_INCREMENT COMMENT '角色id',
  30. `menu_id` bigint(200) NOT NULL DEFAULT '0' COMMENT '权限id',
  31. PRIMARY KEY (`role_id`,`menu_id`)
  32. )ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
  33. CREATE TABLE `sys_user` (
  34. `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
  35. `user_name` varchar(64) NOT NULL DEFAULT 'NULL' COMMENT '用户名',
  36. `nick_name` varchar(64) NOT NULL DEFAULT 'NULL' COMMENT '昵称',
  37. `password` varchar(64) NOT NULL DEFAULT 'NULL' COMMENT '密码',
  38. `status` char(1) DEFAULT '0' COMMENT '帐号状态(0正常 1停用)',
  39. `email` varchar(64) DEFAULT 'NULL' COMMENT '邮箱',
  40. `mobile` varchar(32) DEFAULT 'NULL' COMMENT '手机号',
  41. `sex` char(1) DEFAULT NULL COMMENT '用户性别(0男,1女,2未知)',
  42. `create_time` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
  43. `modify_time` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
  44. `del_flag` int(11) DEFAULT '0' COMMENT '是否删除(0未删除 1已删除)',
  45. `remark` varchar(500) DEFAULT NULL COMMENT '备注',
  46. PRIMARY KEY (`id`)
  47. )ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
  48. CREATE TABLE `sys_user_role` (
  49. `user_id` bigint(200) NOT NULL AUTO_INCREMENT COMMENT '角色id',
  50. `role_id` bigint(200) NOT NULL DEFAULT '0' COMMENT '权限id',
  51. PRIMARY KEY (`user_id`,`role_id`)
  52. )ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
  53. INSERT INTO `zipkin`.`sys_user` (`id`, `user_name`, `password`, `status`, `create_time`, `modify_time`) VALUES ('1', 'admin', '$2a$10$GoLr2BQF77XaqSM9q3ETqu3fsbaIwOddz4YjvxoL8gGVph486OWmC', '1', '2022-05-26 10:42:58', '2022-05-26 10:42:58');

参考资料:

若依VUE分离版框架:

https://www.wpsshop.cn/w/我家小花儿/article/detail/184011

推荐阅读
  

闽ICP备14008679号