如何生成带有一些自定义声明的JWT访问令牌? [英] How to generate a JWT access token with some custom claims in it?
问题描述
我正在尝试让我的授权服务器生成一个JWT访问令牌,其中包含一些自定义声明.
这是授权服务器/auth/token
端点返回的Bearer令牌的样子:51aea31c-6b57-4c80-9d19-a72e15cb2bb7
我发现此令牌有点短,它只是JWT令牌,并且包含我的自定义声明...
并且在随后对资源服务器的请求中使用它时,它会报错:Cannot convert access token to JSON
我正在使用以下依赖项:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.1.RELEASE</version>
<relativePath/>
</parent>
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
<version>2.1.2.RELEASE</version>
</dependency>
授权服务器的配置方式如下:
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.tokenServices(defaultTokenServices())
.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST)
.accessTokenConverter(jwtAccessTokenConverter())
.userDetailsService(userDetailsService);
endpoints
.pathMapping("/oauth/token", RESTConstants.SLASH + DomainConstants.AUTH + RESTConstants.SLASH + DomainConstants.TOKEN);
TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
tokenEnhancerChain.setTokenEnhancers(Arrays.asList(tokenEnhancer(), jwtAccessTokenConverter()));
endpoints
.tokenStore(tokenStore())
.tokenEnhancer(tokenEnhancerChain)
.authenticationManager(authenticationManager);
}
@Bean
@Primary
public DefaultTokenServices defaultTokenServices() {
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(tokenStore());
defaultTokenServices.setSupportRefreshToken(true);
return defaultTokenServices;
}
@Bean
public TokenStore tokenStore() {
return new JwtTokenStore(jwtAccessTokenConverter());
}
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
jwtAccessTokenConverter.setKeyPair(new KeyStoreKeyFactory(new ClassPathResource(jwtProperties.getSslKeystoreFilename()), jwtProperties.getSslKeystorePassword().toCharArray()).getKeyPair(jwtProperties.getSslKeyPair()));
return jwtAccessTokenConverter;
}
@Bean
public TokenEnhancer tokenEnhancer() {
return new CustomTokenEnhancer();
}
它正在使用类:
class CustomTokenEnhancer implements TokenEnhancer {
@Autowired
private TokenAuthenticationService tokenAuthenticationService;
// Add user information to the token
@Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
User user = (User) authentication.getPrincipal();
Map<String, Object> info = new LinkedHashMap<String, Object>(accessToken.getAdditionalInformation());
info.put(CommonConstants.JWT_CLAIM_USER_EMAIL, user.getEmail().getEmailAddress());
info.put(CommonConstants.JWT_CLAIM_USER_FULLNAME, user.getFirstname() + " " + user.getLastname());
info.put("scopes", authentication.getAuthorities().stream().map(s -> s.toString()).collect(Collectors.toList()));
info.put("organization", authentication.getName());
DefaultOAuth2AccessToken customAccessToken = new DefaultOAuth2AccessToken(accessToken);
customAccessToken.setAdditionalInformation(info);
customAccessToken.setExpiration(tokenAuthenticationService.getExpirationDate());
return customAccessToken;
}
}
我也有课:
@Configuration
class CustomOauth2RequestFactory extends DefaultOAuth2RequestFactory {
@Autowired
private TokenStore tokenStore;
@Autowired
private UserDetailsService userDetailsService;
public CustomOauth2RequestFactory(ClientDetailsService clientDetailsService) {
super(clientDetailsService);
}
@Override
public TokenRequest createTokenRequest(Map<String, String> requestParameters, ClientDetails authenticatedClient) {
if (requestParameters.get("grant_type").equals("refresh_token")) {
OAuth2Authentication authentication = tokenStore
.readAuthenticationForRefreshToken(tokenStore.readRefreshToken(requestParameters.get("refresh_token")));
SecurityContextHolder.getContext()
.setAuthentication(new UsernamePasswordAuthenticationToken(authentication.getName(), null,
userDetailsService.loadUserByUsername(authentication.getName()).getAuthorities()));
}
return super.createTokenRequest(requestParameters, authenticatedClient);
}
}
更新:我还尝试了指定自定义声明的另一种方法:
@Component
class CustomAccessTokenConverter extends JwtAccessTokenConverter {
@Autowired
private TokenAuthenticationService tokenAuthenticationService;
@Override
public OAuth2Authentication extractAuthentication(Map<String, ?> claims) {
OAuth2Authentication authentication = super.extractAuthentication(claims);
authentication.setDetails(claims);
return authentication;
}
@Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
User user = (User) authentication.getPrincipal();
Map<String, Object> info = new LinkedHashMap<String, Object>(accessToken.getAdditionalInformation());
info.put(CommonConstants.JWT_CLAIM_USER_EMAIL, user.getEmail().getEmailAddress());
info.put(CommonConstants.JWT_CLAIM_USER_FULLNAME, user.getFirstname() + " " + user.getLastname());
info.put("scopes", authentication.getAuthorities().stream().map(s -> s.toString()).collect(Collectors.toList()));
info.put("organization", authentication.getName());
DefaultOAuth2AccessToken customAccessToken = new DefaultOAuth2AccessToken(accessToken);
customAccessToken.setAdditionalInformation(info);
customAccessToken.setExpiration(tokenAuthenticationService.getExpirationDate());
return super.enhance(customAccessToken, authentication);
}
}
像这样被称呼:
endpoints
.tokenStore(tokenStore())
.tokenEnhancer(jwtAccessTokenConverter())
.accessTokenConverter(jwtAccessTokenConverter())
但是它什么也没改变,并且错误仍然相同.
与调试器一起运行时,这两个增强器替代都不会被调用.
要使用OAuth2,JWT和其他声明构建Spring Boot服务器,我们应该:
1)向项目添加依赖项:
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
<version>2.1.2.RELEASE</version>
</dependency>
2)添加Web安全配置(以发布AuthenticationManager
bean-它将在下一步中使用),例如:
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
@Autowired
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(username -> AuthUser.with()
.username(username)
.password("{noop}" + username)
.email(username + "@mail.com")
.authority(AuthUser.Role.values()[ThreadLocalRandom.current().nextInt(2)])
.build()
);
}
@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
}
这里实现了一个简单的UserDetailsService
以用于测试目的.它与以下简单的用户"对象和实现GrantedAuthority
接口的Role
枚举一起使用. AuthUser
仅具有一个附加属性email
,它将作为声明添加到JWT令牌中.
@Value
@EqualsAndHashCode(callSuper = false)
public class AuthUser extends User {
private String email;
@Builder(builderMethodName = "with")
public AuthUser(final String username, final String password, @Singular final Collection<? extends GrantedAuthority> authorities, final String email) {
super(username, password, authorities);
this.email = email;
}
public enum Role implements GrantedAuthority {
USER, ADMIN;
@Override
public String getAuthority() {
return this.name();
}
}
}
3)配置授权服务器并启用资源服务器:
@Configuration
@EnableAuthorizationServer
@EnableResourceServer
public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {
public static final String TOKEN_KEY = "abracadabra";
private final AuthenticationManager authenticationManager;
public AuthServerConfig(final AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
@Override
public void configure(ClientDetailsServiceConfigurer clientDetailsService) throws Exception {
clientDetailsService.inMemory()
.withClient("client")
.secret("{noop}")
.scopes("*")
.authorizedGrantTypes("password", "refresh_token")
.accessTokenValiditySeconds(60 * 2) // 2 min
.refreshTokenValiditySeconds(60 * 60); // 60 min
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
TokenEnhancerChain chain = new TokenEnhancerChain();
chain.setTokenEnhancers(List.of(tokenEnhancer(), tokenConverter()));
endpoints
.tokenStore(tokenStore())
.reuseRefreshTokens(false)
.tokenEnhancer(chain)
.authenticationManager(authenticationManager);
}
@Bean
public TokenStore tokenStore() {
return new JwtTokenStore(tokenConverter());
}
@Bean
public JwtAccessTokenConverter tokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey(TOKEN_KEY);
converter.setAccessTokenConverter(authExtractor());
return converter;
}
private TokenEnhancer tokenEnhancer() {
return (accessToken, authentication) -> {
if (authentication != null && authentication.getPrincipal() instanceof AuthUser) {
AuthUser authUser = (AuthUser) authentication.getPrincipal();
Map<String, Object> additionalInfo = new HashMap<>();
additionalInfo.put("user_email", authUser.getEmail());
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
}
return accessToken;
};
}
@Bean
public DefaultAccessTokenConverter authExtractor() {
return new DefaultAccessTokenConverter() {
@Override
public OAuth2Authentication extractAuthentication(Map<String, ?> claims) {
OAuth2Authentication authentication = super.extractAuthentication(claims);
authentication.setDetails(claims);
return authentication;
}
};
}
}
此处实现了一个简单的ClientDetailsService
.它仅包含一个客户端,该客户端具有客户端"名称,空白密码以及授予的类型密码"和"refresh_token".它使我们可以创建一个新的访问令牌并刷新它. (要使用多种类型的客户端或在其他情况下,您必须实现更复杂,并且可能是持久性的ClientDetailsService
变体.)
授权端点使用TokenEnhancerChain
配置,其中包含tokenEnhancer
和tokenConverter
.按此顺序添加它们很重要.第一个使用其他声明(在本例中为用户电子邮件)增强了访问令牌.第二个创建一个JWT令牌.通过简单的JwtTokenStore
,我们的TokenEnhancerChain
和authenticationManager
设置的endpoints
.
响应: 如果我们在 https://jwt.io/上解码此访问令牌,我们可以看到它包含 要从传入请求的JWT令牌中提取此类声明(和其他数据),我们可以使用以下方法: 我的工作演示: sb-jwt-oauth-demo >
相关信息: I'm trying to have my authorization server generate a JWT access token with some custom claims in it. Here is what the Bearer token returned by the authorization server I find this token a bit short to be a JWT token and to contain my custom claims... And when using it in subsequent requests to the resource server, it complains with the error: I'm using the following dependencies: The authorization server is configured this way: And it's using the class: I also have the class: UPDATE: I also tried the alternative way of specifying the custom claim: with it being called like: but it changed nothing and the error remained identical. Running with the debugger, none of these two enhancer overrides are called. To build Spring Boot server with OAuth2, JWT and extra claims we should: 1) Add dependency to the project: 2) Add Web security configuration (to publish Here is implemented a simple 3) Configure Authorization server and enable Resource server: A simple Authorization endpoints are configured with Note to The last thing here is Then all things are set up we can request our server to get an access token: Rsponse: If we decode this access token on https://jwt.io/ we can see that it contain the To extract such a claim (and other data) from a JWT token of incoming requests we can use the following approach: My working demo: sb-jwt-oauth-demo Related info: 这篇关于如何生成带有一些自定义声明的JWT访问令牌?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!JwtTokenStore
的注意事项-如果您决定实施商店的持久变体,则可以找到更多信息 curl -i \
--user client: \
-H "Content-Type: application/x-www-form-urlencoded" \
-X POST \
-d "grant_type=password&username=user&password=user&scope=*" \
http://localhost:8080/oauth/token
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2VtYWlsIjoidXNlckBtYWlsLmNvbSIsInVzZXJfbmFtZSI6InVzZXIiLCJzY29wZSI6WyIqIl0sImV4cCI6MTU0Nzc2NDIzOCwiYXV0aG9yaXRpZXMiOlsiQURNSU4iXSwianRpIjoiYzk1YzkzYTAtMThmOC00OGZjLWEzZGUtNWVmY2Y1YWIxMGE5IiwiY2xpZW50X2lkIjoiY2xpZW50In0.RWSGMC0w8tNafT28i2GLTnPnIiXfAlCdydEsNNZK-Lw",
"token_type": "bearer",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2VtYWlsIjoidXNlckBtYWlsLmNvbSIsInVzZXJfbmFtZSI6InVzZXIiLCJzY29wZSI6WyIqIl0sImF0aSI6ImM5NWM5M2EwLTE4ZjgtNDhmYy1hM2RlLTVlZmNmNWFiMTBhOSIsImV4cCI6MTU0Nzc2NzcxOCwiYXV0aG9yaXRpZXMiOlsiQURNSU4iXSwianRpIjoiZDRhNGU2ZjUtNDY2Mi00NGZkLWI0ZDgtZWE5OWRkMDJkYWI2IiwiY2xpZW50X2lkIjoiY2xpZW50In0.m7XvxwuPiTnPaQXAptLfi3CxN3imfQCVKyjmMCIPAVM",
"expires_in": 119,
"scope": "*",
"user_email": "user@mail.com",
"jti": "c95c93a0-18f8-48fc-a3de-5efcf5ab10a9"
}
user_email
声明:
{
"user_email": "user@mail.com",
"user_name": "user",
"scope": [
"*"
],
"exp": 1547764238,
"authorities": [
"ADMIN"
],
"jti": "c95c93a0-18f8-48fc-a3de-5efcf5ab10a9",
"client_id": "client"
}
@RestController
public class DemoController {
@GetMapping("/demo")
public Map demo(OAuth2Authentication auth) {
var details = (OAuth2AuthenticationDetails) auth.getDetails();
//noinspection unchecked
var decodedDetails = (Map<String, Object>) details.getDecodedDetails();
return Map.of(
"name", decodedDetails.get("user_name"),
"email", decodedDetails.get("user_email"),
"roles", decodedDetails.get("authorities")
);
}
}
/auth/token
endpoint looks like: 51aea31c-6b57-4c80-9d19-a72e15cb2bb7
Cannot convert access token to JSON
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.1.RELEASE</version>
<relativePath/>
</parent>
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
<version>2.1.2.RELEASE</version>
</dependency>
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.tokenServices(defaultTokenServices())
.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST)
.accessTokenConverter(jwtAccessTokenConverter())
.userDetailsService(userDetailsService);
endpoints
.pathMapping("/oauth/token", RESTConstants.SLASH + DomainConstants.AUTH + RESTConstants.SLASH + DomainConstants.TOKEN);
TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
tokenEnhancerChain.setTokenEnhancers(Arrays.asList(tokenEnhancer(), jwtAccessTokenConverter()));
endpoints
.tokenStore(tokenStore())
.tokenEnhancer(tokenEnhancerChain)
.authenticationManager(authenticationManager);
}
@Bean
@Primary
public DefaultTokenServices defaultTokenServices() {
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(tokenStore());
defaultTokenServices.setSupportRefreshToken(true);
return defaultTokenServices;
}
@Bean
public TokenStore tokenStore() {
return new JwtTokenStore(jwtAccessTokenConverter());
}
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
jwtAccessTokenConverter.setKeyPair(new KeyStoreKeyFactory(new ClassPathResource(jwtProperties.getSslKeystoreFilename()), jwtProperties.getSslKeystorePassword().toCharArray()).getKeyPair(jwtProperties.getSslKeyPair()));
return jwtAccessTokenConverter;
}
@Bean
public TokenEnhancer tokenEnhancer() {
return new CustomTokenEnhancer();
}
class CustomTokenEnhancer implements TokenEnhancer {
@Autowired
private TokenAuthenticationService tokenAuthenticationService;
// Add user information to the token
@Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
User user = (User) authentication.getPrincipal();
Map<String, Object> info = new LinkedHashMap<String, Object>(accessToken.getAdditionalInformation());
info.put(CommonConstants.JWT_CLAIM_USER_EMAIL, user.getEmail().getEmailAddress());
info.put(CommonConstants.JWT_CLAIM_USER_FULLNAME, user.getFirstname() + " " + user.getLastname());
info.put("scopes", authentication.getAuthorities().stream().map(s -> s.toString()).collect(Collectors.toList()));
info.put("organization", authentication.getName());
DefaultOAuth2AccessToken customAccessToken = new DefaultOAuth2AccessToken(accessToken);
customAccessToken.setAdditionalInformation(info);
customAccessToken.setExpiration(tokenAuthenticationService.getExpirationDate());
return customAccessToken;
}
}
@Configuration
class CustomOauth2RequestFactory extends DefaultOAuth2RequestFactory {
@Autowired
private TokenStore tokenStore;
@Autowired
private UserDetailsService userDetailsService;
public CustomOauth2RequestFactory(ClientDetailsService clientDetailsService) {
super(clientDetailsService);
}
@Override
public TokenRequest createTokenRequest(Map<String, String> requestParameters, ClientDetails authenticatedClient) {
if (requestParameters.get("grant_type").equals("refresh_token")) {
OAuth2Authentication authentication = tokenStore
.readAuthenticationForRefreshToken(tokenStore.readRefreshToken(requestParameters.get("refresh_token")));
SecurityContextHolder.getContext()
.setAuthentication(new UsernamePasswordAuthenticationToken(authentication.getName(), null,
userDetailsService.loadUserByUsername(authentication.getName()).getAuthorities()));
}
return super.createTokenRequest(requestParameters, authenticatedClient);
}
}
@Component
class CustomAccessTokenConverter extends JwtAccessTokenConverter {
@Autowired
private TokenAuthenticationService tokenAuthenticationService;
@Override
public OAuth2Authentication extractAuthentication(Map<String, ?> claims) {
OAuth2Authentication authentication = super.extractAuthentication(claims);
authentication.setDetails(claims);
return authentication;
}
@Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
User user = (User) authentication.getPrincipal();
Map<String, Object> info = new LinkedHashMap<String, Object>(accessToken.getAdditionalInformation());
info.put(CommonConstants.JWT_CLAIM_USER_EMAIL, user.getEmail().getEmailAddress());
info.put(CommonConstants.JWT_CLAIM_USER_FULLNAME, user.getFirstname() + " " + user.getLastname());
info.put("scopes", authentication.getAuthorities().stream().map(s -> s.toString()).collect(Collectors.toList()));
info.put("organization", authentication.getName());
DefaultOAuth2AccessToken customAccessToken = new DefaultOAuth2AccessToken(accessToken);
customAccessToken.setAdditionalInformation(info);
customAccessToken.setExpiration(tokenAuthenticationService.getExpirationDate());
return super.enhance(customAccessToken, authentication);
}
}
endpoints
.tokenStore(tokenStore())
.tokenEnhancer(jwtAccessTokenConverter())
.accessTokenConverter(jwtAccessTokenConverter())
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
<version>2.1.2.RELEASE</version>
</dependency>
AuthenticationManager
bean - it will be used in the next step), for example:@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
@Autowired
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(username -> AuthUser.with()
.username(username)
.password("{noop}" + username)
.email(username + "@mail.com")
.authority(AuthUser.Role.values()[ThreadLocalRandom.current().nextInt(2)])
.build()
);
}
@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
}
UserDetailsService
for testing purpose. It works with the following simple 'User' object and Role
enum which implements GrantedAuthority
interface. AuthUser
has only one additional property email
which will be added to the JWT token as a claim.@Value
@EqualsAndHashCode(callSuper = false)
public class AuthUser extends User {
private String email;
@Builder(builderMethodName = "with")
public AuthUser(final String username, final String password, @Singular final Collection<? extends GrantedAuthority> authorities, final String email) {
super(username, password, authorities);
this.email = email;
}
public enum Role implements GrantedAuthority {
USER, ADMIN;
@Override
public String getAuthority() {
return this.name();
}
}
}
@Configuration
@EnableAuthorizationServer
@EnableResourceServer
public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {
public static final String TOKEN_KEY = "abracadabra";
private final AuthenticationManager authenticationManager;
public AuthServerConfig(final AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
@Override
public void configure(ClientDetailsServiceConfigurer clientDetailsService) throws Exception {
clientDetailsService.inMemory()
.withClient("client")
.secret("{noop}")
.scopes("*")
.authorizedGrantTypes("password", "refresh_token")
.accessTokenValiditySeconds(60 * 2) // 2 min
.refreshTokenValiditySeconds(60 * 60); // 60 min
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
TokenEnhancerChain chain = new TokenEnhancerChain();
chain.setTokenEnhancers(List.of(tokenEnhancer(), tokenConverter()));
endpoints
.tokenStore(tokenStore())
.reuseRefreshTokens(false)
.tokenEnhancer(chain)
.authenticationManager(authenticationManager);
}
@Bean
public TokenStore tokenStore() {
return new JwtTokenStore(tokenConverter());
}
@Bean
public JwtAccessTokenConverter tokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey(TOKEN_KEY);
converter.setAccessTokenConverter(authExtractor());
return converter;
}
private TokenEnhancer tokenEnhancer() {
return (accessToken, authentication) -> {
if (authentication != null && authentication.getPrincipal() instanceof AuthUser) {
AuthUser authUser = (AuthUser) authentication.getPrincipal();
Map<String, Object> additionalInfo = new HashMap<>();
additionalInfo.put("user_email", authUser.getEmail());
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
}
return accessToken;
};
}
@Bean
public DefaultAccessTokenConverter authExtractor() {
return new DefaultAccessTokenConverter() {
@Override
public OAuth2Authentication extractAuthentication(Map<String, ?> claims) {
OAuth2Authentication authentication = super.extractAuthentication(claims);
authentication.setDetails(claims);
return authentication;
}
};
}
}
ClientDetailsService
is implemented here. It contains only one client, which has 'client' name, blank password and granted types "password" and "refresh_token". It gives us a possibility to create a new access token and refresh it. (To work with many types of clients or in other scenarios you have to implement more complex, and maybe persistent, variants of ClientDetailsService
.)TokenEnhancerChain
which contains tokenEnhancer
and tokenConverter
. It's important to add them in this sequence. The first one enhances an access token with additional claims (user email in our case). The second one creates a JWT token. The endpoints
set with a simple JwtTokenStore
, our TokenEnhancerChain
and authenticationManager
.JwtTokenStore
- if you decide to implement a persistent variant of the store you can find more info here.authExtractor
which gives us a possibility to extract claims from JWT tokens of incoming requests.curl -i \
--user client: \
-H "Content-Type: application/x-www-form-urlencoded" \
-X POST \
-d "grant_type=password&username=user&password=user&scope=*" \
http://localhost:8080/oauth/token
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2VtYWlsIjoidXNlckBtYWlsLmNvbSIsInVzZXJfbmFtZSI6InVzZXIiLCJzY29wZSI6WyIqIl0sImV4cCI6MTU0Nzc2NDIzOCwiYXV0aG9yaXRpZXMiOlsiQURNSU4iXSwianRpIjoiYzk1YzkzYTAtMThmOC00OGZjLWEzZGUtNWVmY2Y1YWIxMGE5IiwiY2xpZW50X2lkIjoiY2xpZW50In0.RWSGMC0w8tNafT28i2GLTnPnIiXfAlCdydEsNNZK-Lw",
"token_type": "bearer",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2VtYWlsIjoidXNlckBtYWlsLmNvbSIsInVzZXJfbmFtZSI6InVzZXIiLCJzY29wZSI6WyIqIl0sImF0aSI6ImM5NWM5M2EwLTE4ZjgtNDhmYy1hM2RlLTVlZmNmNWFiMTBhOSIsImV4cCI6MTU0Nzc2NzcxOCwiYXV0aG9yaXRpZXMiOlsiQURNSU4iXSwianRpIjoiZDRhNGU2ZjUtNDY2Mi00NGZkLWI0ZDgtZWE5OWRkMDJkYWI2IiwiY2xpZW50X2lkIjoiY2xpZW50In0.m7XvxwuPiTnPaQXAptLfi3CxN3imfQCVKyjmMCIPAVM",
"expires_in": 119,
"scope": "*",
"user_email": "user@mail.com",
"jti": "c95c93a0-18f8-48fc-a3de-5efcf5ab10a9"
}
user_email
claim:{
"user_email": "user@mail.com",
"user_name": "user",
"scope": [
"*"
],
"exp": 1547764238,
"authorities": [
"ADMIN"
],
"jti": "c95c93a0-18f8-48fc-a3de-5efcf5ab10a9",
"client_id": "client"
}
@RestController
public class DemoController {
@GetMapping("/demo")
public Map demo(OAuth2Authentication auth) {
var details = (OAuth2AuthenticationDetails) auth.getDetails();
//noinspection unchecked
var decodedDetails = (Map<String, Object>) details.getDecodedDetails();
return Map.of(
"name", decodedDetails.get("user_name"),
"email", decodedDetails.get("user_email"),
"roles", decodedDetails.get("authorities")
);
}
}