我们有一个权利数据库,其中包含应用程序 ID、角色以及映射到每个应用程序角色的用户。遵循关于线程的建议如何根据resourceId将用户角色映射到oauth2范围/权限?
忽略我上面提到的权利数据库,我是否可以根据下面代码中的用户和资源 ID 将角色“USER”、“READER”、“WRITER”映射到 oauth2 范围/权限?
用户认证/授权配置
@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”,但对于“code”授权则失败。对于“代码”授权,DefaultOAuth2RequestFactory 尝试在授权步骤中为客户端(acme)而非用户映射权限。 另一种想法是实现ClientDetailsService,根据登录用户(管理员/用户)添加客户端(acme)的权限,但不确定如何从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()));
}
...
}
登录配置
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
}
}
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;
}
现在客户端拥有所需的权限 user 和 admin。
并配置您的请求工厂:
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
DefaultOAuth2RequestFactory defaultOAuth2RequestFactory = new DefaultOAuth2RequestFactory(clientDetailsService());
defaultOAuth2RequestFactory.setCheckUserScopes(true);
endpoints.requestFactory(defaultOAuth2RequestFactory);
}