我已经使用Sping Boot 3.1.0和Spring OAuth2 Authorization Server 1.1.0创建了一个Spring OAuth2 Authorization Server。这工作正常,我收到一个访问令牌使用授权授予类型,看起来像下面这样:
{
"access_token": "eyJraWQiOiJiZGM5MTVlMy01ZjliLTQzYWItOTU5Yi0zYzJmNTE3YTI0NDgiLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJhZG1pbiIsImF1ZCI6ImNsaWVudDEiLCJuYmYiOjE2ODU1NDEwOTYsInNjb3BlIjpbInJlYWQiLCJvcGVuaWQiXSwiaXNzIjoiaHR0cDovL2F1dGgtc2VydmVyOjgwMDAiLCJleHAiOjE2ODU1NDEzOTYsImlhdCI6MTY4NTU0MTA5Nn0.JtQSGR4LlYIZU0NV16ht4-LU0fRDUs9yD33NHFY1nItEc3NUs6vbV8SeSPGbmdMpxUMcr1_Xd1FpSkKrWbPPBZC10hortVrA1k550wGLVrZcknsc7sW10G718dLlJvL7qJGj4sqrqLIP1vVR8Ft3M7CdoT34Z7z6-JcHKRgmnXOP-tyvdWhRtn_OVb1o_29pTumJQ9GPSHU_Z6miOrDvOgUllWUwypw9Cg6aJJyl403P0Cl2wYye4HvP0gfosq6qbNy5OTZ4yiG0HrxrsYvNux9JIvYGbxMUhp9pNF84d3NOzvc24aDxD_VkerBV3zlfrOgLOtSstRdwLxaJ7dc-4Q",
"refresh_token": "VIAvsVUef8ljKBBPvv9gi1-DU48U77h8lZ0OBh0HO57fyGxJTppazUMOlAfnAsCrvMGc5XFVlX1Lii04YltVjF4dk-vrJWHEplhKtIehxxZXEX3HmTqaSL63pYQq9cGr",
"scope": "read openid",
"id_token": "eyJraWQiOiJiZGM5MTVlMy01ZjliLTQzYWItOTU5Yi0zYzJmNTE3YTI0NDgiLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJhZG1pbiIsImF1ZCI6ImNsaWVudDEiLCJhenAiOiJjbGllbnQxIiwiYXV0aF90aW1lIjoxNjg1NTQxMDY3LCJpc3MiOiJodHRwOi8vYXV0aC1zZXJ2ZXI6ODAwMCIsImV4cCI6MTY4NTU0Mjg5NiwiaWF0IjoxNjg1NTQxMDk2LCJzaWQiOiJrUllCUjFBNWJPbDZJR0FGYnZTNHJZSDc0dG5ncHhCemh2eWNCV0ZLR1dnIn0.LIjMH6ONDGSBE2pO3sDUsPmDsstJhvQb6NPRrDZO8TAClyNpwMMRkCmPociU2Jv_rjQq8Y-zXrj016WchkGgCeakCyItzCvpTmqUDjM9tHwpG7FWuDC_GBsFstLwHqussVOG23vvy2KyNi6h8EMtbIR_aqFbDfzvknXQkAK-8Hl2ICqPfbzDkcZeomvV9J07ScqCL6iMkWw3g8ISJfvmWtiymuQ3tGa_9qJXA-JcgcZJhYGpSCbd052AxerZTMpJC4tN1afJDCfJy0HrgnChdX1wp_r9QXLKbNb1SEGRd8IUWzOLRHkOiJKqlgFx-AzuQ7sVINYjHHE1A8yHSGqGSQ",
"token_type": "Bearer",
"expires_in": 299
}
现在,正如你看到的,这是一个令牌与非常小的信息.例如,用户角色、用户电子邮件等等。为此,我想定制令牌的创建。为了实现这一点,我尝试使用OAuth2TokenCustomizer类。但由于某种原因,它不起作用。
我创建了以下Bean
@Bean
public OAuth2TokenCustomizer<OAuth2TokenClaimsContext> accessTokenCustomizer() {
return context -> {
var userName = context.getPrincipal().getName();
StringBuilder roles = new StringBuilder();
var authorities = context.getPrincipal().getAuthorities();
if(OAuth2TokenType.ACCESS_TOKEN.equals(context.getTokenType())) {
context.getClaims().claims(claims -> {
claims.put("userName", userName);
claims.put("testVal", "This is a test string");
for (var auth:authorities) {
if(roles.isEmpty()){
roles.append(auth.getAuthority());
} else {
roles.append(" ").append(auth.getAuthority());
}
}
claims.put("roles", roles);
});
} };
}
此Bean在OAuth2TokenGenerator Bean中使用,如下所示
@Bean
public OAuth2TokenGenerator<OAuth2Token> tokenGenerator() {
JwtEncoder jwtEncoder = new NimbusJwtEncoder(jwkSource());
JwtGenerator jwtGenerator = new JwtGenerator(jwtEncoder);
OAuth2AccessTokenGenerator accessTokenGenerator = new OAuth2AccessTokenGenerator();
accessTokenGenerator.setAccessTokenCustomizer(accessTokenCustomizer());
OAuth2RefreshTokenGenerator refreshTokenGenerator = new OAuth2RefreshTokenGenerator();
return new DelegatingOAuth2TokenGenerator(
jwtGenerator, accessTokenGenerator, refreshTokenGenerator);
}
这样不行我尝试了一些其他选项,比如这里给出的https://github.com/spring-projects/spring-authorization-server/issues/925(jgrandja在2022年10月25日给出的答案),但当我这样做时,我的应用无法自动连接OAuth2AuthorizationService Bean
我不知道我在这里错过了什么。如果有人能帮上忙,我会非常感激。我的两个配置类文件如下:
AuthorizationServerConfig.java
package com.auth.config;
import java.util.UUID;
import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.SecurityContext;
import com.smarc.auth.config.keys.KeyManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.core.OAuth2Token;
import org.springframework.security.oauth2.core.oidc.OidcScopes;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.JwtEncoder;
import org.springframework.security.oauth2.jwt.NimbusJwtEncoder;
import org.springframework.security.oauth2.server.authorization.OAuth2TokenType;
import org.springframework.security.oauth2.server.authorization.client.InMemoryRegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2AuthorizationServerConfigurer;
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
import org.springframework.security.oauth2.server.authorization.settings.ClientSettings;
import org.springframework.security.oauth2.server.authorization.token.DelegatingOAuth2TokenGenerator;
import org.springframework.security.oauth2.server.authorization.token.JwtGenerator;
import org.springframework.security.oauth2.server.authorization.token.OAuth2AccessTokenGenerator;
import org.springframework.security.oauth2.server.authorization.token.OAuth2RefreshTokenGenerator;
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenClaimsContext;
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenCustomizer;
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
public class AuthorizationServerConfig
{
private final KeyManager keyManager;
public AuthorizationServerConfig(KeyManager keyManager)
{
this.keyManager = keyManager;
}
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SecurityFilterChain customSecurityFilterChainOAuth(HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
http.getConfigurer(OAuth2AuthorizationServerConfigurer.class).oidc(Customizer.withDefaults());
http.getConfigurer(OAuth2AuthorizationServerConfigurer.class).tokenGenerator(tokenGenerator());
return http.formLogin(Customizer.withDefaults()).build();
}
@Bean
public RegisteredClientRepository registeredClientRepository() {
RegisteredClient registeredClient = RegisteredClient.withId(UUID.randomUUID().toString())
.clientId("client1")
.clientSecret("{noop}myClientSecretValue")
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST)
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
.redirectUri("http://127.0.0.1:8080/login/oauth2/code/users-client-oidc")
.redirectUri("http://127.0.0.1:8080/authorized")
.scope(OidcScopes.OPENID)
.scope("read")
.build();
return new InMemoryRegisteredClientRepository(registeredClient);
}
@Bean
public ClientSettings clientSettings() {
return ClientSettings.builder()
.requireAuthorizationConsent(false)
.requireProofKey(false)
.build();
}
@Bean
public AuthorizationServerSettings authorizationServerSettings() {
return AuthorizationServerSettings.builder()
.issuer("http://localhost:8080").build();
}
@Bean
public JWKSource<SecurityContext> jwkSource() {
JWKSet set = new JWKSet(keyManager.rsaKey());
return (j, jc) -> j.select(set);
}
@Bean
public JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {
return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);
}
@Bean
public OAuth2TokenGenerator<OAuth2Token> tokenGenerator() {
JwtEncoder jwtEncoder = new NimbusJwtEncoder(jwkSource());
JwtGenerator jwtGenerator = new JwtGenerator(jwtEncoder);
OAuth2AccessTokenGenerator accessTokenGenerator = new OAuth2AccessTokenGenerator();
accessTokenGenerator.setAccessTokenCustomizer(accessTokenCustomizer());
OAuth2RefreshTokenGenerator refreshTokenGenerator = new OAuth2RefreshTokenGenerator();
return new DelegatingOAuth2TokenGenerator(
jwtGenerator, accessTokenGenerator, refreshTokenGenerator);
}
@Bean
public OAuth2TokenCustomizer<OAuth2TokenClaimsContext> accessTokenCustomizer() {
return context -> {
var userName = context.getPrincipal().getName();
StringBuilder roles = new StringBuilder();
var authorities = context.getPrincipal().getAuthorities();
if(OAuth2TokenType.ACCESS_TOKEN.equals(context.getTokenType())) {
context.getClaims().claims(claims -> {
claims.put("userName", userName);
claims.put("testVal", "This is a test string");
for (var auth:authorities) {
if(roles.isEmpty()){
roles.append(auth.getAuthority());
} else {
roles.append(" ").append(auth.getAuthority());
}
}
claims.put("roles", roles);
});
} };
}
}
WebSecurityConfig.java
package com.auth.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity
public class WebSecurityConfig
{
@Bean
public SecurityFilterChain customSecurityFilterChain(HttpSecurity http) throws Exception{
return http
.authorizeHttpRequests(authorizeRequests -> authorizeRequests.anyRequest().authenticated())
.formLogin(Customizer.withDefaults()).build();
}
@Bean
public UserDetailsService userDetailsService() {
var externalUser = User.withUsername("external").password(passwordEncoder().encode("12345"))
.roles("read").build();
var internalUser = User.withUsername("internal").password(passwordEncoder().encode("12345"))
.roles("read", "write").build();
var admin = User.withUsername("admin").password(passwordEncoder().encode("12345"))
.roles("read").build();
var userDetailsService = new InMemoryUserDetailsManager();
userDetailsService.createUser(externalUser);
userDetailsService.createUser(internalUser);
userDetailsService.createUser(admin);
return userDetailsService;
}
@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
}
请让我知道如果需要更多的信息。先谢谢你了。
1条答案
按热度按时间6mzjoqzu1#
您注册的bean(
OAuth2TokenCustomizer<OAuth2TokenClaimsContext>
)用于自定义不透明令牌(token format = reference),但看起来您使用的是默认令牌格式jwt(token format = self contained)。如果您想使用不透明令牌(我假设您不想),请查看文档了解如何为每个客户端定制令牌格式。如参考中所述,对于您的情况,您可以简单地注册一个
OAuth2TokenCustomizer<JwtEncodingContext>
类型的bean来定制JWT。此外,文档还指出:
如果
OAuth2TokenGenerator
未作为@Bean
提供或未通过OAuth2AuthorizationServerConfigurer
进行配置,则OAuth2TokenCustomizer<JwtEncodingContext>
@Bean
将自动配置为JwtGenerator
。这意味着,如果您只想自定义令牌声明,而不是生成令牌的整个过程,您可以简单地发布
OAuth2TokenCustomizer
@Bean
,并省略OAuth2TokenGenerator
@Bean
。因此,您的配置应该简单地具有:
我认为这一点可以在文档中更清楚一点,因为示例演示了同时定制两者,但注解指定了两者都不是必需的。