我可以在颁发访问令牌时包含用户信息吗?
can I include user information while issuing an access token?
我在一些 oauth2 实现中看到了有关授权服务器在发出访问令牌时返回的响应的附加信息。我想知道是否有办法使用 spring-security-oauth2 来完成此操作。我希望能够在访问令牌响应中包含一些用户权限,这样我的消费应用程序就不需要管理用户权限,但仍然可以在他们自己的安全上下文中设置用户并应用他们自己的任何 spring-安全检查。
- 我如何获得有关访问令牌响应的信息?
- 我如何在 oauth2 客户端拦截该信息并将其设置在安全上下文中?
我想另一种选择是使用 JWT 令牌并与客户端应用程序共享适当的信息,以便他们可以从令牌中解析用户/权限并将其设置在上下文中。这让我更加不舒服,因为我更愿意控制哪些客户端应用程序可以访问此信息(仅限受信任的应用程序)并且 AFAIK 只有授权服务器和资源服务器应该知道如何解析 JWT 令牌。
您需要像这样实现自定义 TokenEnhancer:
public class CustomTokenEnhancer implements TokenEnhancer {
@Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
User user = (User) authentication.getPrincipal();
final Map<String, Object> additionalInfo = new HashMap<>();
additionalInfo.put("customInfo", "some_stuff_here");
additionalInfo.put("authorities", user.getAuthorities());
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
return accessToken;
}
}
并将其作为具有相应设置器的 bean 添加到您的 AuthorizationServerConfigurerAdapter
@Configuration
@EnableAuthorizationServer
protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
// Some autowired stuff here
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
// @formatter:off
endpoints
// ...
.tokenEnhancer(tokenEnhancer());
// @formatter:on
}
@Bean
@Primary
public AuthorizationServerTokenServices tokenServices() {
DefaultTokenServices tokenServices = new DefaultTokenServices();
// ...
tokenServices.setTokenEnhancer(tokenEnhancer());
return tokenServices;
}
// Some @Bean here like tokenStore
@Bean
public TokenEnhancer tokenEnhancer() {
return new CustomTokenEnhancer();
}
}
然后在控制器中(例如)
@RestController
public class MyController {
@Autowired
private AuthorizationServerTokenServices tokenServices;
@RequestMapping(value = "/getSomething", method = RequestMethod.GET)
public String getSection(OAuth2Authentication authentication) {
Map<String, Object> additionalInfo = tokenServices.getAccessToken(authentication).getAdditionalInformation();
String customInfo = (String) additionalInfo.get("customInfo");
Collection<? extends GrantedAuthority> authorities = (Collection<? extends GrantedAuthority>) additionalInfo.get("authorities");
// Play with authorities
return customInfo;
}
}
我个人使用 JDBC TokenStore,所以我的 "Some autowired stuff here" 对应于一些 @Autowired 数据源、PasswordEncoder 等等。
希望对您有所帮助!
package com.security;
import java.util.HashMap;
import java.util.Map;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.stereotype.Component;
@Component
public class CustomTokenEnhancer implements TokenEnhancer {
@Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken,
OAuth2Authentication authentication) {
// TODO Auto-generated method stub
User user = (User) authentication.getPrincipal();
final Map<String, Object> additionalInfo = new HashMap<>();
additionalInfo.put("customInfo", "some_stuff_here");
additionalInfo.put("authorities", user.getAuthorities());
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
return accessToken;
}
}
以下是xml配置:
<bean id="tokenEnhancer" class="com.security.CustomTokenEnhancer" />
<!-- Used to create token and and every thing about them except for their persistence that is reposibility of TokenStore (Given here is a default implementation) -->
<bean id="tokenServices" class="org.springframework.security.oauth2.provider.token.DefaultTokenServices">
<property name="tokenStore" ref="tokenStore" />
<property name="accessTokenValiditySeconds" value="30000000"></property>
<property name="refreshTokenValiditySeconds" value="300000000"></property>
<property name="supportRefreshToken" value="true"></property>
<property name="clientDetailsService" ref="clientDetails"></property>
<property name="tokenEnhancer" ref="tokenEnhancer" />
</bean>
这就是我能够向令牌添加额外信息的方式。
连同:
@Bean
public TokenEnhancer tokenEnhancer() {
return new CustomTokenEnhancer();
}
你必须包括
@Bean
public DefaultAccessTokenConverter accessTokenConverter() {
return new DefaultAccessTokenConverter();
}
并将所有内容添加到端点配置:
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.tokenStore(tokenStore)
.tokenEnhancer(tokenEnhancer())
.accessTokenConverter(accessTokenConverter())
.authorizationCodeServices(codeServices)
.authenticationManager(authenticationManager)
;
}
没有它,您的 CustomTokenEnhancer 将无法工作。
如果您正在使用 Spring 的 JwtAccessTokenConverter
或 DefaultAccessTokenConverter
,您可以添加您的自定义 CustomTokenEnhancer(请参阅第一个回复)并申请它使用 TokenEnhancerChain 像这样:
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
enhancerChain.setTokenEnhancers(Arrays.asList(customTokenEnhancer(), accessTokenConverter()));
endpoints.tokenStore(tokenStore())
.tokenEnhancer(enhancerChain)
.authenticationManager(authenticationManager);
}
@Bean
protected JwtAccessTokenConverter jwtTokenEnhancer() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey("my_signing_key");
return converter;
}
@Bean public TokenEnhancer customTokenEnhancer() {
return new CustomTokenEnhancer();
}
另一种解决方案是创建一个自定义 TokenConverter,它扩展 Spring 的 JwtAccessTokenConverter
并使用您的自定义声明覆盖 enhance() 方法。
public class CustomTokenConverter extends JwtAccessTokenConverter {
@Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
final Map<String, Object> additionalInfo = new HashMap<>();
additionalInfo.put("customized", "true");
User user = (User) authentication.getPrincipal();
additionalInfo.put("isAdmin", user.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList()).contains("BASF_ADMIN"));
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
return super.enhance(accessToken, authentication);
}
}
然后:
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(tokenStore())
.tokenEnhancer(customTokenEnhancer())
.authenticationManager(authenticationManager);
}
@Bean public CustomTokenConverter customTokenEnhancer() {
return new CustomTokenConverter();
}
- 创建一个 class 文件 CustomTokenEnhancer
@Component
public class CustomTokenConverter extends JwtAccessTokenConverter {
@Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
final Map<String, Object> additionalInfo = new HashMap<>();
additionalInfo.put("customized", "true");
User user = (User) authentication.getPrincipal();
additionalInfo.put("role", user.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList()));
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
return super.enhance(accessToken, authentication);
}
}
- 将下面的代码粘贴到 AuthorizationServerConfig
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
enhancerChain.setTokenEnhancers(Arrays.asList(customTokenEnhancer(),accessTokenConverter()));
endpoints
.tokenStore(tokenStore())
.tokenEnhancer(customTokenEnhancer())
.authenticationManager(authenticationManager);
}
@Bean
protected JwtAccessTokenConverter jwtTokenEnhancer() {
JwtAccessTokenConverter converter= new JwtAccessTokenConverter();
converter.setSigningKey("my_signing_key");
return converter;
}
@Bean
public CustomTokenConverter customTokenEnhancer() {
return new CustomTokenConverter();
}
@Bean
public TokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}
- 粘贴以上代码后导入合适的库
output response of Custom Token Enhancer..click here
我在排除UserDetailsServiceAutoConfiguration时解决了这个问题。
像这样。也许会对 OAuth2 资源服务器有所帮助。
@SpringBootApplication(exclude = [UserDetailsServiceAutoConfiguration::class])
class Application
fun main(args: Array<String>) {
runApplication<Application>(*args)
}
我在一些 oauth2 实现中看到了有关授权服务器在发出访问令牌时返回的响应的附加信息。我想知道是否有办法使用 spring-security-oauth2 来完成此操作。我希望能够在访问令牌响应中包含一些用户权限,这样我的消费应用程序就不需要管理用户权限,但仍然可以在他们自己的安全上下文中设置用户并应用他们自己的任何 spring-安全检查。
- 我如何获得有关访问令牌响应的信息?
- 我如何在 oauth2 客户端拦截该信息并将其设置在安全上下文中?
我想另一种选择是使用 JWT 令牌并与客户端应用程序共享适当的信息,以便他们可以从令牌中解析用户/权限并将其设置在上下文中。这让我更加不舒服,因为我更愿意控制哪些客户端应用程序可以访问此信息(仅限受信任的应用程序)并且 AFAIK 只有授权服务器和资源服务器应该知道如何解析 JWT 令牌。
您需要像这样实现自定义 TokenEnhancer:
public class CustomTokenEnhancer implements TokenEnhancer {
@Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
User user = (User) authentication.getPrincipal();
final Map<String, Object> additionalInfo = new HashMap<>();
additionalInfo.put("customInfo", "some_stuff_here");
additionalInfo.put("authorities", user.getAuthorities());
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
return accessToken;
}
}
并将其作为具有相应设置器的 bean 添加到您的 AuthorizationServerConfigurerAdapter
@Configuration
@EnableAuthorizationServer
protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
// Some autowired stuff here
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
// @formatter:off
endpoints
// ...
.tokenEnhancer(tokenEnhancer());
// @formatter:on
}
@Bean
@Primary
public AuthorizationServerTokenServices tokenServices() {
DefaultTokenServices tokenServices = new DefaultTokenServices();
// ...
tokenServices.setTokenEnhancer(tokenEnhancer());
return tokenServices;
}
// Some @Bean here like tokenStore
@Bean
public TokenEnhancer tokenEnhancer() {
return new CustomTokenEnhancer();
}
}
然后在控制器中(例如)
@RestController
public class MyController {
@Autowired
private AuthorizationServerTokenServices tokenServices;
@RequestMapping(value = "/getSomething", method = RequestMethod.GET)
public String getSection(OAuth2Authentication authentication) {
Map<String, Object> additionalInfo = tokenServices.getAccessToken(authentication).getAdditionalInformation();
String customInfo = (String) additionalInfo.get("customInfo");
Collection<? extends GrantedAuthority> authorities = (Collection<? extends GrantedAuthority>) additionalInfo.get("authorities");
// Play with authorities
return customInfo;
}
}
我个人使用 JDBC TokenStore,所以我的 "Some autowired stuff here" 对应于一些 @Autowired 数据源、PasswordEncoder 等等。
希望对您有所帮助!
package com.security;
import java.util.HashMap;
import java.util.Map;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.stereotype.Component;
@Component
public class CustomTokenEnhancer implements TokenEnhancer {
@Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken,
OAuth2Authentication authentication) {
// TODO Auto-generated method stub
User user = (User) authentication.getPrincipal();
final Map<String, Object> additionalInfo = new HashMap<>();
additionalInfo.put("customInfo", "some_stuff_here");
additionalInfo.put("authorities", user.getAuthorities());
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
return accessToken;
}
}
以下是xml配置:
<bean id="tokenEnhancer" class="com.security.CustomTokenEnhancer" />
<!-- Used to create token and and every thing about them except for their persistence that is reposibility of TokenStore (Given here is a default implementation) -->
<bean id="tokenServices" class="org.springframework.security.oauth2.provider.token.DefaultTokenServices">
<property name="tokenStore" ref="tokenStore" />
<property name="accessTokenValiditySeconds" value="30000000"></property>
<property name="refreshTokenValiditySeconds" value="300000000"></property>
<property name="supportRefreshToken" value="true"></property>
<property name="clientDetailsService" ref="clientDetails"></property>
<property name="tokenEnhancer" ref="tokenEnhancer" />
</bean>
这就是我能够向令牌添加额外信息的方式。
连同:
@Bean
public TokenEnhancer tokenEnhancer() {
return new CustomTokenEnhancer();
}
你必须包括
@Bean
public DefaultAccessTokenConverter accessTokenConverter() {
return new DefaultAccessTokenConverter();
}
并将所有内容添加到端点配置:
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.tokenStore(tokenStore)
.tokenEnhancer(tokenEnhancer())
.accessTokenConverter(accessTokenConverter())
.authorizationCodeServices(codeServices)
.authenticationManager(authenticationManager)
;
}
没有它,您的 CustomTokenEnhancer 将无法工作。
如果您正在使用 Spring 的 JwtAccessTokenConverter
或 DefaultAccessTokenConverter
,您可以添加您的自定义 CustomTokenEnhancer(请参阅第一个回复)并申请它使用 TokenEnhancerChain 像这样:
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
enhancerChain.setTokenEnhancers(Arrays.asList(customTokenEnhancer(), accessTokenConverter()));
endpoints.tokenStore(tokenStore())
.tokenEnhancer(enhancerChain)
.authenticationManager(authenticationManager);
}
@Bean
protected JwtAccessTokenConverter jwtTokenEnhancer() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey("my_signing_key");
return converter;
}
@Bean public TokenEnhancer customTokenEnhancer() {
return new CustomTokenEnhancer();
}
另一种解决方案是创建一个自定义 TokenConverter,它扩展 Spring 的 JwtAccessTokenConverter
并使用您的自定义声明覆盖 enhance() 方法。
public class CustomTokenConverter extends JwtAccessTokenConverter {
@Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
final Map<String, Object> additionalInfo = new HashMap<>();
additionalInfo.put("customized", "true");
User user = (User) authentication.getPrincipal();
additionalInfo.put("isAdmin", user.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList()).contains("BASF_ADMIN"));
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
return super.enhance(accessToken, authentication);
}
}
然后:
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(tokenStore())
.tokenEnhancer(customTokenEnhancer())
.authenticationManager(authenticationManager);
}
@Bean public CustomTokenConverter customTokenEnhancer() {
return new CustomTokenConverter();
}
- 创建一个 class 文件 CustomTokenEnhancer
@Component
public class CustomTokenConverter extends JwtAccessTokenConverter {
@Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
final Map<String, Object> additionalInfo = new HashMap<>();
additionalInfo.put("customized", "true");
User user = (User) authentication.getPrincipal();
additionalInfo.put("role", user.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList()));
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
return super.enhance(accessToken, authentication);
}
}
- 将下面的代码粘贴到 AuthorizationServerConfig
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
enhancerChain.setTokenEnhancers(Arrays.asList(customTokenEnhancer(),accessTokenConverter()));
endpoints
.tokenStore(tokenStore())
.tokenEnhancer(customTokenEnhancer())
.authenticationManager(authenticationManager);
}
@Bean
protected JwtAccessTokenConverter jwtTokenEnhancer() {
JwtAccessTokenConverter converter= new JwtAccessTokenConverter();
converter.setSigningKey("my_signing_key");
return converter;
}
@Bean
public CustomTokenConverter customTokenEnhancer() {
return new CustomTokenConverter();
}
@Bean
public TokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}
- 粘贴以上代码后导入合适的库
output response of Custom Token Enhancer..click here
我在排除UserDetailsServiceAutoConfiguration时解决了这个问题。 像这样。也许会对 OAuth2 资源服务器有所帮助。
@SpringBootApplication(exclude = [UserDetailsServiceAutoConfiguration::class])
class Application
fun main(args: Array<String>) {
runApplication<Application>(*args)
}