Mapping user roles to oauth2 scopes/authorities

安稳与你 提交于 2019-12-17 19:33:30

问题


We have a entitlements database which has application id, roles and users mapped to roles per application. Following the advice on thread how do I map user roles to oauth2 scopes/authorities based on resourceId?

Ignoring the entitlements database I mentioned above do I map roles "USER", "READER", "WRITER" to oauth2 scopes/authorities based on user and resourceId in below code?

User Authentication/Authorization Config

@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 Config

@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()");
    }
}

Update 1:

Introduced a custom OAuth2RequestFactory into the config to set checkUserScopes to true. While this setup works for "client_credentails", it fails for "code" grants. For "code" grants, DefaultOAuth2RequestFactory tries to maps authorities for client (acme) rather than user during the authorization step. Other thought is to implement ClientDetailsService that add authorities of client (acme) based on the logged-in user (admin/user) but not sure on how to grab logged-in user from SecurityContext since it is overwritten with client (acme) during authorization step. Any ideas?

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;
    }

}

and updated related code to

@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
    }
}

回答1:


I ran into the same problem and I also noticed the code was running the checkUserScopes method twice. I found out that what is missing is that both the user AND the CLIENT need to have the authorities that you want to return.

So define your client in a way like this (adjust roles to your own):

    @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;
    }

Now the client has the required authorities user and admin.

And configure your request factory:

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {

        DefaultOAuth2RequestFactory defaultOAuth2RequestFactory = new DefaultOAuth2RequestFactory(clientDetailsService());
        defaultOAuth2RequestFactory.setCheckUserScopes(true);
        endpoints.requestFactory(defaultOAuth2RequestFactory);
    }


来源:https://stackoverflow.com/questions/31345466/mapping-user-roles-to-oauth2-scopes-authorities

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!