微服务OAuth 2.1认证授权Demo方案(Spring Security 6)
书接上文
微服务OAuth 2.1认证授权可行性方案(Spring Security 6)
一、介绍
三个微服务
auth
微服务作为认证服务器,用于颁发JWT
。gateway
微服务作为网关,用于拦截过滤。content
微服务作为资源服务器,用于校验授权。
以下是授权相关数据库。
user
表示用户表role
表示角色表user_role
关联了用户和角色,表示某个用户是是什么角色。一个用户可以有多个角色menu
表示资源权限表。@PreAuthorize("hasAuthority('xxx')")
时用的就是这里的code
。permission
关联了角色和资源权限,表示某个角色用于哪些资源访问权限,一个角色有多个资源访问权限。
当我们知道userId
,我们就可以知道这个用户可以访问哪些资源,并把这些权限(也就是menu
里的code
字段)写成数组,写到JWT
的负载部分的authorities
字段中。当用户携带此JWT访问具有@PreAuthorize("hasAuthority('xxx')")
修饰的资源时,我们解析出JWT
中的authorities
字段,判断是否包含hasAuthority
指定的xxx
权限,以此来完成所谓的的”授权”。
二、auth微服务代码
1. SecurityConfig
1 | package com.xuecheng.auth.config; |
这里需要注意几点
- 使用
BCryptPasswordEncoder
密码加密,在设置clientSecret
时需要手动使用密码编码器。 jwtTokenCustomizer
解析UserDetails
然后往JWT
中添加authorities
字段,为了后面的授权。
2. UserDetailsService
1 | package com.xuecheng.ucenter.service.impl; |
这里需要注意几点
username
就是前端/auth/login
的时候输入的账户名。myAuthService.execute(username)
不抛异常,就默认表示账户存在,此时将password
加入UserDetails
并返回,Spring Authorization Server
对比校验两个密码。myAuthService.execute(username)
根据username
获取用户信息返回,将用户信息存入withUsername
中,Spring Authorization Server
默认会将其加入到JWT
中。- 现在
Spring Authorization Server
默认不会把authorities(permissions)
写入JWT
,需要配合OAuth2TokenCustomizer
手动写入。
3. 总结
这样,auth
微服务颁发的JWT
,现在就会包含authorities
字段。示例如下
1 | { |
三、gateway微服务代码
1. 统一处理CORS问题
1 | @EnableWebFluxSecurity |
这里需要注意几点
- 书接上文,这里虽然用了
oauth2.jwt(Customizer.withDefaults())
,但实际上基于远程auth
微服务开放的jwkSetEndpoint
配置的JwtDecoder
。 .cors(cors -> cors.configurationSource(corsConfigurationSource()))
一次性处理CORS
问题。
四、content微服务代码
1. controller
1 | @PreAuthorize("hasAuthority('xc_teachmanager_course_list')") |
使用了@PreAuthorize("hasAuthority('xc_teachmanager_course_list')")
修饰的controller
资源。
2. SecurityConfig
1 | @Configuration |
需要注意几点
- 使用
@EnableMethodSecurity
让@PreAuthorize
生效 - 和
gateway
一样,需要基于远程auth
微服务开放的jwkSetEndpoint
配置JwtDecoder
。 - 指定
JwtAuthenticationConverter
,让anyRequest().authenticated()
需要验证的请求,除了完成默认的JWT
验证外,还需要完成JwtAuthenticationConverter
指定逻辑。 JwtAuthenticationConverter
中将JWT
的authorities
部分形成数组后写入GrantedAuthorities
,这正是spring security6
用于校验@PreAuthorize
的字段。
3. 解析JWT Utils
1 | @Slf4j |
把JWT
的信息解析回XcUser
,相当于用户携带JWT
访问后端,后端可以根据JWT
获取此用户的信息。当然,你可以尽情的自定义,扩展。
4. 总结
当用户携带JWT
访问需要权限的资源时,现在可以正常的校验权限了。
五、一些坑
- 写
RegisteredClient
时注册那么多redirectUri
是因为debug
了很久,才发现获取授权码和获取JWT
时,redirect_uri
参数需要一致。 cors
问题,spring secuity6
似乎会一开始直接默认拒绝cors
,导致跨域请求刚到gateway
就寄了,到不了content
微服务,即使content
微服务配置了CORS
的处理方案,也无济于事。