我们有一个权利数据库,其中包含应用程序ID,角色和用户映射到每个应用程序的角色。在advice on thread之后如何根据resourceId将用户角色映射到oauth2范围/权限?
忽略我上面提到的权利数据库,我是否将角色“USER”,“READER”,“WRITER”映射到oauth2范围/权限基于以下代码中的user和resourceId ?
用户身份验证/授权配置
@Configuration
@Order(-10)
protected static class LoginConfig extends WebSecurityConfigurerAdapter {
....
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// auth.parentAuthenticationManager(authenticationManager);
// @formatter:off
auth.inMemoryAuthentication()
.withUser("admin").password("admin")
.roles("ADMIN", "USER", "READER", "WRITER")
.and()
.withUser("user").password("password")
.roles("USER")
.and()
.withUser("audit").password("audit")
.roles("USER", "ADMIN", "READER");
// @formatter:on
}
}
OAuth2配置
@Configuration
@EnableAuthorizationServer
protected static class OAuth2Config extends AuthorizationServerConfigurerAdapter {
@Autowired
private AuthenticationManager authenticationManager;
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// @formatter:off
clients.inMemory()
.withClient("acme").secret("acmesecret")
.authorizedGrantTypes("authorization_code", "refresh_token", "password")
.scopes("openid")
.and()
.withClient("trusted").secret("shuush")
.authorizedGrantTypes("client_credentials")
.scopes("openid");
// @formatter:on
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager);
}
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.checkTokenAccess("isAuthenticated()");
}
}
更新1:
在配置中引入了自定义OAuth2RequestFactory,将checkUserScopes设置为true。虽然此设置适用于“client_credentails”,但它无法获得“代码”授权。对于“代码”授权,DefaultOAuth2RequestFactory在授权步骤期间尝试maps authorities为客户端(acme)而不是用户。 其他想法是实现基于登录用户(admin / user)添加客户端(acme)权限的ClientDetailsService,但不确定如何从SecurityContext中获取登录用户,因为它被客户端覆盖( acme)在授权步骤期间。有什么想法吗?
public class ScopeMappingOAuth2RequestFactory extends DefaultOAuth2RequestFactory {
private SecurityContextAccessor securityContextAccessor = new DefaultSecurityContextAccessor();
public ScopeMappingOAuth2RequestFactory(ClientDetailsService clientDetailsService) {
super(clientDetailsService);
super.setCheckUserScopes(true);
}
/**
* @param securityContextAccessor the security context accessor to set
*/
@Override
public void setSecurityContextAccessor(SecurityContextAccessor securityContextAccessor) {
this.securityContextAccessor = securityContextAccessor;
super.setSecurityContextAccessor(securityContextAccessor);
}
@Override
public AuthorizationRequest createAuthorizationRequest(Map<String, String> authorizationParameters) {
AuthorizationRequest request = super.createAuthorizationRequest(authorizationParameters);
if (securityContextAccessor.isUser()) {
request.setAuthorities(securityContextAccessor.getAuthorities());
}
return request;
}
}
并将相关代码更新为
@EnableAuthorizationServer
protected static class OAuth2Config extends AuthorizationServerConfigurerAdapter {
@Autowired
private AuthenticationManager authenticationManager;
private InMemoryClientDetailsService clientDetailsService;
private Map<String, ClientDetails> clientDetailsStore;
public InMemoryClientDetailsService clientDetailsService() {
if (clientDetailsService == null) {
clientDetailsStore = new HashMap<String, ClientDetails>();
InMemoryClientDetailsService m = new InMemoryClientDetailsService() {
@Override
public ClientDetails loadClientByClientId(String clientId) throws ClientRegistrationException {
ClientDetails details = clientDetailsStore.get(clientId);
if (details == null) {
throw new NoSuchClientException("No client with requested id: " + clientId);
}
return details;
}
};
clientDetailsService = m;
}
return clientDetailsService;
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
InMemoryClientDetailsServiceBuilder builder = new InMemoryClientDetailsServiceBuilder() {
@Override
protected void addClient(String clientId, ClientDetails value) {
clientDetailsStore.put(clientId, value);
}
@Override
protected ClientDetailsService performBuild() {
return clientDetailsService();
}
};
clients.setBuilder(builder);
// @formatter:off
builder
.withClient("acme").secret("acmesecret")
.authorizedGrantTypes("authorization_code", "refresh_token", "password")
.scopes("openid", "apim.read", "apim.write")
.and()
.withClient("trusted").secret("shuush")
.authorizedGrantTypes("client_credentials")
.scopes("openid", "apim.read", "apim.write")
.authorities("openid", "apim.read", "apim.write");
// @formatter:on
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager);
endpoints.requestFactory(new ScopeMappingOAuth2RequestFactory(clientDetailsService()));
}
... }
LoginConfig
Configuration
@Order(-10)
protected static class LoginConfig extends WebSecurityConfigurerAdapter {
....
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// auth.parentAuthenticationManager(authenticationManager);
// @formatter:off
auth.inMemoryAuthentication()
.withUser("admin").password("admin")
.roles("APIM.READ", "APIM.WRITE")
.and()
.withUser("user").password("password")
.roles("APIM.READ")
.and()
.withUser("audit").password("audit")
.roles("APIM.READ");
// @formatter:on
}
}
答案 0 :(得分:0)
我遇到了同样的问题,并且我还注意到代码两次运行checkUserScopes
方法。我发现缺少的是用户和客户端都需要具有要返回的权限。
因此以这种方式定义您的客户(将角色调整为自己的角色):
@Bean
public ClientDetailsService clientDetailsService() {
Map<String, ClientDetails> clientDetailsStore = new HashMap<>();
Collection<String> scope = new HashSet<>();
scope.add("user");
scope.add("admin");
Collection<GrantedAuthority> authorities = new HashSet<>();
authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
authorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
Collection<String> authorizedGrantTypes = new HashSet<>();
authorizedGrantTypes.add("authorization_code");
BaseClientDetails clientDetails = new BaseClientDetails();
clientDetails.setClientId("clientid");
clientDetails.setClientSecret("{noop}secret"); //noop for Spring Security 5
clientDetails.setScope(scope);
clientDetails.setAuthorities(authorities);
clientDetails.setAuthorizedGrantTypes(authorizedGrantTypes);
clientDetailsStore.put("clientid", clientDetails);
InMemoryClientDetailsService clientDetailsService = new InMemoryClientDetailsService();
clientDetailsService.setClientDetailsStore(clientDetailsStore);
return clientDetailsService;
}
现在,客户端具有所需的权限用户和管理员。
并配置您的请求工厂:
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
DefaultOAuth2RequestFactory defaultOAuth2RequestFactory = new DefaultOAuth2RequestFactory(clientDetailsService());
defaultOAuth2RequestFactory.setCheckUserScopes(true);
endpoints.requestFactory(defaultOAuth2RequestFactory);
}
答案 1 :(得分:0)
我一直在遇到同样的问题,因为默认情况下securityContext具有客户端详细信息,所以我扩展了DefaultOauth2RequestFactory并在SecurityContext中手动设置了用户身份验证
public TokenRequest createTokenRequest(Map<String, String> requestParameters, ClientDetails authenticatedClient) {
SecurityContextHolder.getContext()
.setAuthentication(new UsernamePasswordAuthenticationToken(requestParameters.get("username"), null,
userDetailsService.loadUserByUsername(requestParameters.get("username")).getAuthorities()));
return super.createTokenRequest(requestParameters, authenticatedClient);
}
使用此代码后,始终会通过用户身份验证而不是客户端身份验证来填充SecurityContext 您也可以针对特定的赠款类型执行此操作
if (requestParameters.get("grant_type").equals("password")) { //same code as above }