游客发表

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

发帖时间:2025-11-04 19:25:24

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

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

用法

集成

这就是个月个明一个Spring Boot Starter,你自己打包、最后做出智安装。经过决定然后引用到项目:

<dependency>             <groupId>cn.felord.security</groupId>             <artifactId>security-enhance-spring-boot-starter</artifactId>             <version>${version}</version>         </dependency> 

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

<dependency>       <groupId>org.springframework.boot</groupId>       <artifactId>spring-boot-starter-cache</artifactId>   </dependency>   <dependency>       <groupId>org.springframework.boot</groupId>       <artifactId>spring-boot-starter-data-redis</artifactId>   </dependency>   <dependency>       <groupId>org.apache.commons</groupId>       <artifactId>commons-pool2</artifactId>   </dependency> 

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

@Bean     @ConditionalOnMissingBean     public JwtTokenStorage jwtTokenStorage() {         return new SpringCacheJwtTokenStorage();     } 

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

数据库表设计

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

用户表:

user_id username password 1312434534 felord {noop}12345

角色表:

role_id role_name role_code 12343667867 管理员 ADMIN

用户角色关联表:

user_role_id user_id role_id 12354657777 1312434534 12343667867

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

资源表:

resources_id resources_name resource_pattern method 12543667867 根据ID获取商品 /goods/{goodsId} GET

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

角色资源表:

role_res_id role_id resources_id 4545466445 12343667867 12543667867

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

实现UserDetailsService

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

@Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {     UserInfo userInfo = this.lambdaQuery()             .eq(UserInfo::getUsername, username).one();     if (Objects.isNull(userInfo)) {         throw new UsernameNotFoundException("用户:" + username + " 不存在");     }     String userId = userInfo.getUserId();     boolean enabled = userInfo.getEnabled();     Set<String> roles = iUserRoleService.getRolesByUserId(userId);     roles.add(“"ANONYMOUS"”);     Set<GrantedAuthority> roleSet = roles.stream()             .map(role -> new SimpleGrantedAuthority("ROLE_" + role))             .collect(Collectors.toSet());     return new SecureUser(userId,             username,             userInfo.getSecret(),             enabled,             enabled,             enabled,             enabled,             roleSet); } 

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

查询用户的权限集

实现用户角色权限方法Function

配置

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

# jwt 配置 jwt:   cert-info:    # keytool 密钥的 alias      alias: felord     # 密匙密码     key-password: i6x123akg15v13     # 路径 这里是在resources 包下     cert-location: jwt.jks   claims:     # jwt iss 字段值     issuer: https://felord.cn     # sub 字段     subject: all     # 过期秒数     expires-at: 604800 

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

@EnableSpringSecurity @Configuration(proxyBeanMethods = false) public class SecurityConfiguration {     /**      * Function function.      *      * @param resourcesService the resources service      * @return the function      */     @Bean     Function<Set<String>, Set<AntPathRequestMatcher>> function(IResourcesService resourcesService){         return resourcesService::matchers;     }     @Bean     UserDetailsService userDetailsService(IUserInfoService userInfoService){         return userInfoService::loadUserByUsername;      } } 

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

使用

登录接口

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

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

accessToken 用来日常进行请求鉴权,有过期时间。 refreshToken 当accessToken过期失效时,用来刷新accessToken。

结构为:

{   "accessToken": {     "tokenValue": "",     "issuedAt": {       "epochSecond": 1616827822,       "nano": 393000000     },     "expiresAt": {       "epochSecond": 1616831422,       "nano": 393000000     },     "tokenType": {       "value": "Bearer"     },     "scopes": [       "ROLE_ADMIN",       "ROLE_ANONYMOUS"     ]   },   "refreshToken": {     "tokenValue": "",     "issuedAt": {       "epochSecond": 1616827822,       "nano": 393000000     },     "expiresAt": null   },   "additionalParameters": {} } 

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

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

本文转载自微信公众号「码农小胖哥」,可以通过以下二维码关注。转载本文请联系码农小胖哥公众号。IT技术网

    热门排行

    友情链接