经过我翻来覆去的思想斗争了一个月,最后做出了一个明智的决定

本文分享了一位开发者如何将SpringSecurity模块化,实现简单配置即可使用的JWT和RBAC动态权限管理组件。通过创建SpringBoot Starter,集成SpringCache(如RedisCache),并提供了数据库表设计、UserDetailsService实现、权限查询及配置示例。登录接口返回JWT Token,包括accessToken和refreshToken,使得API调用鉴权更为便捷。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近写了几个Spring Boot组件,项目用什么功能就引入对应的依赖,配置配置就能使用,香的很!那么Spring Security能不能也弄成模块化,简单配置一下就可以用上呢?JWT得有,RBAC动态权限更得有!花了小半天就写了个组件,用了一个月感觉还不错。是我一个人爽?还是放出来让大家一起爽?经过我翻来覆去的思想斗争了一个月,最后做出了一个明智的决定,放出来让想直接上手的同学直接使用。源码地址就在下面:

https://gitee.com/felord/security-enhance-spring-boot

用法

集成

这就是一个Spring Boot Starter,你自己打包、安装。然后引用到项目:


     
  1.         <dependency>
  2.             <groupId>cn.felord.security</groupId>
  3.             <artifactId>security-enhance-spring-boot-starter</artifactId>
  4.             <version>${version}</version>
  5.         </dependency>

另外你需要集成Spring Cache,比如Redis Cache:


     
  1.     <dependency>
  2.         <groupId>org.springframework.boot</groupId>
  3.         <artifactId>spring-boot-starter-cache</artifactId>
  4.     </dependency>
  5.     <dependency>
  6.         <groupId>org.springframework.boot</groupId>
  7.         <artifactId>spring-boot-starter-data-redis</artifactId>
  8.     </dependency>
  9.     <dependency>
  10.         <groupId>org.apache.commons</groupId>
  11.         <artifactId>commons-pool2</artifactId>
  12.     </dependency>

JWT会被缓存到以usrTkn为key的缓存中,如果你想定制的话,自行实现一个JwtTokenStorage并注入Spring IoC就可以覆盖下面的配置了:


     
  1.     @Bean
  2.     @ConditionalOnMissingBean
  3.     public JwtTokenStorage jwtTokenStorage() {
  4.          return  new SpringCacheJwtTokenStorage();
  5.     }

你应该去了解如何自定义Spring Cache的过期时间。

数据库表设计

然后是数据库表设计,这里简单点弄个RBAC的设计,仅供参考,你可以根据你们的业务改良。

用户表

user_idusernamepassword
1312434534felord{noop}12345

角色表

role_idrole_namerole_code
12343667867管理员ADMIN

用户角色关联表

user_role_iduser_idrole_id
12354657777131243453412343667867

一个用户可以持有多个角色,一个角色在一个用户持有的角色集合中是唯一的。

资源表

resources_idresources_nameresource_patternmethod
12543667867根据ID获取商品/goods/{goodsId}GET

资源其实就是我们写的Spring MVC接口,这里支持ANT风格,但是尽量具体,为了灵活性考虑不推荐使用通配符。

角色资源表

role_res_idrole_idresources_id
45454664451234366786712543667867

一个资源可以关联多个角色,一个角色不能重复持有一个资源。

实现UserDetailsService

实现用户加载服务接口UserDetailsService是Spring Security开发的必要步骤,跟我以前的教程差不多。


     
  1. @Override
  2. public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
  3.     UserInfo userInfo = this.lambdaQuery()
  4.             .eq(UserInfo::getUsername, username).one();
  5.      if (Objects.isNull(userInfo)) {
  6.         throw  new UsernameNotFoundException( "用户:" + username +  " 不存在");
  7.     }
  8.     String userId = userInfo.getUserId();
  9.     boolean enabled = userInfo.getEnabled();
  10.     Set<String> roles = iUserRoleService.getRolesByUserId(userId);
  11.     roles.add(“ "ANONYMOUS"”);
  12.     Set<GrantedAuthority> roleSet = roles.stream()
  13.             . map(role ->  new SimpleGrantedAuthority( "ROLE_" + role))
  14.             .collect(Collectors.toSet());
  15.      return  new SecureUser(userId,
  16.             username,
  17.             userInfo.getSecret(),
  18.             enabled,
  19.             enabled,
  20.             enabled,
  21.             enabled,
  22.             roleSet);
  23. }

这里要说一下里面为啥要内置一个ANONYMOUS角色给用户。如果希望特定的资源对用户全量开放,可配置对应的权限角色编码为ANONYMOUS。当某个资源的角色编码为ANONYMOUS时,即使不携带Token也可以访问。一般情况下匿名能访问的资源不匿名一定能访问,当然你如果不希望这样的规则存在干掉就是了。

查询用户的权限集

实现用户角色权限方法Function<Set<String>, Set<AntPathRequestMatcher>>并注入Spring IoC,根据用户持有的角色集合查询用户可访问的资源列表。这个基于前几天的动态权限文章实现的具体可以去了解。也可以根据当前资源的AntPathRequestMatcher来查询用户是否持有对应的角色,这个你自行改造。

配置

最后就是配置了,跟我以前教程中的配置几乎一样,application.yaml的配置为:


     
  1. # jwt 配置
  2. jwt:
  3.   cert-info:
  4.    # keytool 密钥的 alias 
  5.     alias: felord
  6.     # 密匙密码
  7.     key-password: i6x123akg15v13
  8.     # 路径 这里是在resources 包下
  9.     cert-location: jwt.jks
  10.   claims:
  11.     # jwt iss 字段值
  12.     issuer: https: //felord.cn
  13.     # sub 字段
  14.     subject: all
  15.     # 过期秒数
  16.     expires-at:  604800

最后别忘记弄个配置类并标记@EnableSpringSecurity以启用配置:


     
  1. @EnableSpringSecurity
  2. @Configuration(proxyBeanMethods =  false)
  3. public class SecurityConfiguration {
  4.      /**
  5.      * Function function.
  6.      *
  7.      * @param resourcesService the resources service
  8.      * @return the function
  9.      */
  10.     @Bean
  11.     Function<Set<String>, Set<AntPathRequestMatcher>> function(IResourcesService resourcesService){
  12.          return resourcesService::matchers;
  13.     }
  14.     
  15.     @Bean
  16.     UserDetailsService userDetailsService(IUserInfoService userInfoService){
  17.          return userInfoService::loadUserByUsername; 
  18.     }
  19. }

记得使用@EnableCaching开启并配置缓存。

使用

登录接口


     
  1. POST /login?username=felord&password= 12345 HTTP/ 1.1
  2. Host: localhost: 8080

然后会返回一对JWT,返回包含两个token主体

  • accessToken 用来日常进行请求鉴权,有过期时间。

  • refreshTokenaccessToken过期失效时,用来刷新accessToken

结构为:


     
  1. {
  2.    "accessToken": {
  3.      "tokenValue""",
  4.      "issuedAt": {
  5.        "epochSecond"1616827822,
  6.        "nano"393000000
  7.     },
  8.      "expiresAt": {
  9.        "epochSecond"1616831422,
  10.        "nano"393000000
  11.     },
  12.      "tokenType": {
  13.        "value""Bearer"
  14.     },
  15.      "scopes": [
  16.        "ROLE_ADMIN",
  17.        "ROLE_ANONYMOUS"
  18.     ]
  19.   },
  20.    "refreshToken": {
  21.      "tokenValue""",
  22.      "issuedAt": {
  23.        "epochSecond"1616827822,
  24.        "nano"393000000
  25.     },
  26.      "expiresAt": null
  27.   },
  28.    "additionalParameters": {}
  29. }

调用根据ID获取商品接口时加入Token:


     
  1. GET /goods/ 234355451 HTTP/ 1.1
  2. Host: localhost: 8080
  3. Authorization: Bearer eyJraWQImFsZyI6IlJTMjU2In0.eyJzdWIiOiJ1NzgsImlhdCI6MTYxNjkxODk3OCwianRpIjoiNThlOTQktNGVlYzc3MDU0ZDk3In0.ZQcN0FX7_taohqPiC1KnoF7

是不是简单多了?觉得好就给个关注、点赞、转发、再看

我踩过的Spring Boot统一返回体中的坑

2021-04-25

Spring Security 实战干货:Spring Security中的单元测试

2021-04-23

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值