Spring Security - 来自 JWT 的自定义主体
Posted
技术标签:
【中文标题】Spring Security - 来自 JWT 的自定义主体【英文标题】:Spring Security - Custom Principal from JWT 【发布时间】:2021-03-31 15:30:02 【问题描述】:我在我的 Angular 应用程序中使用 oauth2 和 sso。我的其余后端验证每个请求发送的身份验证令牌。现在我想使用 oid 声明从我的数据库中加载用户并将其保存在主体中。我还想在“GrantedAuthorities”中添加用户权限。
FooService
[...]
public Foo getFooByOid(String oid) throws FooNotFoundException
return fooRepository.findByOid(oid)
.orElseThrow(() -> new FooNotFoundException("Foo with oid: " + oid + " not found"));
安全配置
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter
@Override
protected void configure(HttpSecurity http) throws Exception
http.authorizeRequests(auth -> auth
.anyRequest().authenticated())
.oauth2ResourceServer(oauth2 ->
oauth2.jwt();
);
spring.security.oauth2.resourceserver.jwt.jwk-set-uri 和 spring.security.oauth2.resourceserver.jwt.issuer-uri 在 application.properties 中定义
之后,我的主体包含所有声明、标头和令牌:
Principal
谁能帮我怎么办?谢谢
【问题讨论】:
【参考方案1】:如果您编写如下代码,您可以通过自定义 AuthenticationManager 执行此操作:
.oauth2ResourceServer.authenticationManagerResolver(request -> new CustomAuthenticationManager(request));
您在 oauth2ResourceServer 上设置的属性会影响 Spring 的 BearerTokenAuthenticationFilter class 的行为
然后您需要自己验证 JWT,并在顶部添加您的自定义声明处理,然后使用相同的令牌缓存后续请求的结果,这很棘手。
我的例子
我有一个相当完整的示例,它的行为类似于这样,它可以在您的 PC 上运行,并且您可以从中借鉴一些想法 - 虽然它是一个相当高级的示例:
Spring Security Configuration【讨论】:
【参考方案2】:Gary Archer 的回答是正确的,但也有一些陷阱。
一个完整的 Kotlin 简单示例:
特定于您的应用程序的用户界面:
interface User
val id : String
val email : String
val displayName : String
val avatar : String
val roles : Set<String>
val domain : String
get() = this.email.split('@').last()
主体应实现此接口:
class ClaimsNotFull(claim: String) : Exception("Claims not full, not found $claim")
data class IdelPrincipal(
override val id: String,
override val email: String,
override val displayName: String,
override val avatar: String,
override val roles: Set<String>,
) : User
companion object
fun fromJwt(jwt: Jwt): IdelPrincipal
fun loadClaim(key: String): String = jwt.claims.getOrElse(key) throw ClaimsNotFull(key) as String
return IdelPrincipal(
id = jwt.subject,
email = loadClaim("email"),
displayName = loadClaim("displayName"),
avatar = loadClaim("avatar"),
roles = loadClaim("roles").split(",").toSet()
)
fun copyToClaims(user : User, jwt: JWTClaimsSet.Builder) : JWTClaimsSet.Builder
return jwt.subject(user.id)
.claim("email", user.email)
.claim("displayName",user.displayName)
.claim("avatar",user.avatar)
.claim("roles",user.roles.joinToString(","))
自定义令牌和 JwtConvertor:
class IdelAuthenticationToken(val jwt: Jwt, val user: IdelPrincipal) :
AbstractAuthenticationToken(IdelAuthorities.from(user.roles))
override fun getCredentials() = jwt // borrowed from JwtAuthenticationToken
override fun getPrincipal() = user
override fun isAuthenticated() = true // decoding of jwt is authentication
class IdelPrincipalJwtConvertor : Converter<Jwt, IdelAuthenticationToken>
override fun convert(jwt: Jwt): IdelAuthenticationToken
val principal = IdelPrincipal.fromJwt(jwt)
return IdelAuthenticationToken(jwt,principal)
配置 Spring Security:
override fun configure(http: HttpSecurity)
http.authorizeRequests
it.anyRequest().authenticated()
.csrfit.ignoringAntMatchers("/token")
.oauth2ResourceServer it.jwt()
.oauth2ResourceServer().jwt cstm ->
cstm.jwtAuthenticationConverter(IdelPrincipalJwtConvertor())
http.exceptionHandling
it
.authenticationEntryPoint(BearerTokenAuthenticationEntryPoint())
.accessDeniedHandler(BearerTokenAccessDeniedHandler())
http .sessionManagement it.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
http.anonymous().disable()
以及控制器中用于测试的方法:
@GetMapping("/me2")
fun me2(@AuthenticationPrincipal user : User) : User
val result = MeResult(
id = user.id,
domain = user.domain,
displayName = user.displayName,
avatar = user.avatar,
email = user.email,
authorities = user.roles.toList()
)
return user
那就是回报:
"id": "10777",
"email": "leonid.vygovsky@gmail.com",
"displayName": "Leonid Vygovskiy",
"avatar": "",
"roles": [
"ROLE_USER"
],
"domain": "gmail.com"
【讨论】:
以上是关于Spring Security - 来自 JWT 的自定义主体的主要内容,如果未能解决你的问题,请参考以下文章
社交登录,spring-security-oauth2 和 spring-security-jwt?
如何在spring security中为来自两个不同表的不同用户配置身份验证?
带有 spring-boot 和 spring-security 的 JWT
Spring Security OAuth2 v5:NoSuchBeanDefinitionException:'org.springframework.security.oauth2.jwt.Jwt