Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Upgrading Spring Security OAuth2

I am trying to upgrade Spring security OAuth2 configuration from 2.0.0.RC1 to 2.0.3.RELEASE. At the time I copied the configuration from sprklr sample and made it work. So it's based on a working example of xml based Spring Security OAuth2 configuration.

Now, I've upgraded to Spring Security latest release (2.0.3 at the time of this writing) and also trying to convert this to java config. I am posting the xml config and the java config below.

 <beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns:security="http://www.springframework.org/schema/security"
xmlns:oauth="http://www.springframework.org/schema/security/oauth2"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2-2.0.xsd">

<!-- Authentication manager. -->
<security:authentication-manager alias="authenticationManager">
    <security:authentication-provider>
        <security:jdbc-user-service data-source-ref="dataSource"
           users-by-username-query="select a.username, a.password, a.enabled, a.email from account a where a.username = ?" 
           authorities-by-username-query="select a.username, r.role_name from account a, role r, account_role ar where a.id = ar.account_id and r.id = ar.role_id and a.username = ?" />
        <security:password-encoder ref="passwordEncoder"/>
    </security:authentication-provider>
</security:authentication-manager>

<security:global-method-security secured-annotations="enabled" jsr250-annotations="enabled" pre-post-annotations="enabled" />


<security:http pattern="/oauth/token" create-session="stateless" authentication-manager-ref="clientAuthenticationManager">
    <security:intercept-url pattern="/oauth/token" access="IS_AUTHENTICATED_FULLY" />
    <security:anonymous enabled="false" />
    <security:http-basic entry-point-ref="clientAuthenticationEntryPoint" />
    <!-- include this only if you need to authenticate clients via request parameters -->
    <security:custom-filter ref="clientCredentialsTokenEndpointFilter" after="BASIC_AUTH_FILTER" />
    <security:access-denied-handler ref="oauthAccessDeniedHandler" />
</security:http>

<security:http pattern="/api/.*/accounts" request-matcher="regex" create-session="stateless" entry-point-ref="oauthAuthenticationEntryPoint" use-expressions="true">
    <security:intercept-url pattern="/api/.*/accounts" method="POST" requires-channel="https" access="#oauth2.clientHasRole('ROLE_CLIENT') or hasRole('ROLE_ANONYMOUS')"/>
    <security:intercept-url pattern="/.*" access="denyAll()" />
    <security:custom-filter ref="resourceServerFilter" before="PRE_AUTH_FILTER" />
    <security:access-denied-handler ref="oauthAccessDeniedHandler" />
    <security:expression-handler ref="oauthWebExpressionHandler" />
</security:http>

<security:http pattern="/api/**" create-session="never" entry-point-ref="oauthAuthenticationEntryPoint" access-decision-manager-ref="accessDecisionManager">
    <security:anonymous enabled="false" />
    <security:intercept-url pattern="/api/**" access="ROLE_USER,SCOPE_TRUST,SCOPE_PLAY"/>
    <security:custom-filter ref="resourceServerFilter" before="PRE_AUTH_FILTER" />
    <security:access-denied-handler ref="oauthAccessDeniedHandler" />
</security:http>

<bean id="oauthAuthenticationEntryPoint" class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
    <property name="realmName" value="*****" />
</bean>

<bean id="clientAuthenticationEntryPoint" class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
    <property name="realmName" value="*****/client" />
    <property name="typeName" value="Basic" />
</bean>

<bean id="oauthAccessDeniedHandler" class="org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler" />

<bean id="clientCredentialsTokenEndpointFilter" class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter">
    <property name="authenticationManager" ref="clientAuthenticationManager" />
</bean>

<bean id="accessDecisionManager" class="org.springframework.security.access.vote.UnanimousBased">
    <constructor-arg>
        <list>
            <bean class="org.springframework.security.oauth2.provider.vote.ScopeVoter" />
            <bean class="org.springframework.security.access.vote.RoleVoter" />
            <bean class="org.springframework.security.access.vote.AuthenticatedVoter" />
        </list>
    </constructor-arg>
</bean>

<security:authentication-manager id="clientAuthenticationManager">
    <security:authentication-provider user-service-ref="clientDetailsUserService" />
</security:authentication-manager>

<bean id="clientDetailsUserService" class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService">
    <constructor-arg ref="clientDetails" />
</bean>

<bean id="tokenStore" class="org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore" />

<bean id="tokenServices" class="org.springframework.security.oauth2.provider.token.DefaultTokenServices">
    <property name="tokenStore" ref="tokenStore" />
    <property name="tokenEnhancer" ref="tokenEnhancer" />
    <property name="supportRefreshToken" value="true" />
    <property name="clientDetailsService" ref="clientDetails" />
    <property name="accessTokenValiditySeconds" value="6000" />
</bean>

<bean id="tokenEnhancer" class="com.****.*****.config.*****TokenEnhancer" />

<bean id="requestFactory" class="org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory">
    <constructor-arg name="clientDetailsService" ref="clientDetails" />
</bean>

<bean id="userApprovalHandler" class="com.****.*****.config.*****UserApprovalHandler">
    <property name="approvalStore" ref="approvalStore" />
    <property name="clientDetailsService" ref="clientDetails" />
    <property name="requestFactory" ref="requestFactory" />
</bean>

<bean id="approvalStore" class="org.springframework.security.oauth2.provider.approval.TokenApprovalStore">
    <property name="tokenStore" ref="tokenStore" />
</bean>

<oauth:authorization-server
    client-details-service-ref="clientDetails" token-services-ref="tokenServices"
    user-approval-handler-ref="userApprovalHandler">
    <oauth:authorization-code />
    <oauth:implicit />
    <oauth:refresh-token />
    <oauth:client-credentials />
    <oauth:password />
</oauth:authorization-server>

<oauth:resource-server id="resourceServerFilter" resource-id="*****" token-services-ref="tokenServices" />

<oauth:client-details-service id="clientDetails">
   <oauth:client client-id="*****-php-demo" secret="??????????"
        authorized-grant-types="password,authorization_code,refresh_token,client_credentials"
        authorities="ROLE_USER, ROLE_CLIENT"
        scope="play,trust"
        access-token-validity="6000"/>


   <oauth:client client-id="*****-swagger-ui"
        authorized-grant-types="implicit"
        authorities="ROLE_USER, ROLE_CLIENT, ROLE_TRUSTED_CLIENT"
        scope="play,trust"
        redirect-uri="${baseUrl}/o2c.html"
        autoapprove="true"
        access-token-validity="6000"/>

   <oauth:client client-id="*****-runscope" 
        secret="???????????????????????????????"
        authorized-grant-types="password,authorization_code,refresh_token"
        authorities="ROLE_USER, ROLE_CLIENT, ROLE_TRUSTED_CLIENT"
        scope="play,trust"
        redirect-uri="https://www.runscope.com/oauth_tool/callback"
        autoapprove="true"
        access-token-validity="6000"/>     

</oauth:client-details-service>  

<oauth:expression-handler id="oauthExpressionHandler" />

<oauth:web-expression-handler id="oauthWebExpressionHandler" /> 

and the java config (so far..)

@Configuration
public class SecurityConfig {

@Autowired
private DataSource dataSource;

@Value("${baseUrl}") private String baseUrl;

@Bean
public ClientDetailsService clientDetailsService() throws Exception {
    ClientDetailsServiceConfiguration serviceConfig = new ClientDetailsServiceConfiguration();

    serviceConfig.clientDetailsServiceConfigurer().inMemory()
        .withClient("*****-????????")
        .secret("????????????????????")
        .authorizedGrantTypes("password", "authorization_code", "refresh_token", "client_credentials")
        .authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
        .scopes("play", "trust")
    .and()
        .withClient("*****-????????")
        .authorizedGrantTypes("implicit")
        .authorities("ROLE_USER", "ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
        .scopes("play", "trust")
        .redirectUris(baseUrl + "/o2c.html")
        .autoApprove(true)
    .and()
        .withClient("*****-???????")
        .secret("????????????????????")
        .authorizedGrantTypes("password", "authorization_code", "refresh_token")
        .authorities("ROLE_USER", "ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
        .scopes("play", "trust")
        .redirectUris("https://www.runscope.com/oauth_tool/callback")
        .autoApprove(true);

    return serviceConfig.clientDetailsService();
}

@Bean
public TokenStore tokenStore() {
    return new InMemoryTokenStore();
}

@Bean
public DefaultTokenServices tokenServices() throws Exception {
    DefaultTokenServices tokenServices = new DefaultTokenServices();
    tokenServices.setAccessTokenValiditySeconds(6000);
    tokenServices.setClientDetailsService(clientDetailsService());
    tokenServices.setTokenEnhancer(new *****TokenEnhancer());
    tokenServices.setSupportRefreshToken(true);
    tokenServices.setTokenStore(tokenStore());
    return tokenServices;
}

@Bean
public UserApprovalHandler userApprovalHandler() throws Exception {
    *****UserApprovalHandler handler = new *****UserApprovalHandler();
    handler.setApprovalStore(approvalStore());
    handler.setClientDetailsService(clientDetailsService());
    handler.setRequestFactory(new DefaultOAuth2RequestFactory(clientDetailsService()));
    return handler;
}

@Bean
public ApprovalStore approvalStore() {
    TokenApprovalStore store = new TokenApprovalStore();
    store.setTokenStore(tokenStore());
    return store;
}

@Bean
public OAuth2AccessDeniedHandler oAuth2AccessDeniedHandler() {
    return new OAuth2AccessDeniedHandler();
}


@Configuration
@Order(Ordered.HIGHEST_PRECEDENCE)
@EnableWebSecurity
protected static class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private DataSource dataSource;

    @Resource
    private PasswordEncoder passwordEncoder;

    @Autowired
    private ClientDetailsService clientDetailsService;

    @Autowired
    private OAuth2AccessDeniedHandler oAuth2AccessDeniedHandler;

    @Bean
    protected UserDetailsService clientDetailsUserService() {
        return new ClientDetailsUserDetailsService(clientDetailsService);
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {

        auth.userDetailsService(clientDetailsUserService());

        JdbcUserDetailsManagerConfigurer<AuthenticationManagerBuilder> jdbcUserDetail = new JdbcUserDetailsManagerConfigurer<AuthenticationManagerBuilder>();
        jdbcUserDetail.dataSource(dataSource);
        jdbcUserDetail.passwordEncoder(passwordEncoder);
        jdbcUserDetail.authoritiesByUsernameQuery("select a.username, r.role_name from account a, role r, account_role ar where a.id = ar.account_id and r.id = ar.role_id and a.username = ?");
        jdbcUserDetail.usersByUsernameQuery("select a.username, a.password, a.enabled, a.email from account a where a.username = ?");

        auth.apply(jdbcUserDetail);
    }

    @Bean(name="authenticationManager")
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Bean
    protected AuthenticationEntryPoint authenticationEntryPoint() {
        OAuth2AuthenticationEntryPoint entryPoint = new OAuth2AuthenticationEntryPoint();
        entryPoint.setTypeName("Basic");
        entryPoint.setRealmName("oauth2/client");
        return entryPoint;
    }

    @Override
    public void configure(WebSecurity webSecurity) throws Exception {
        webSecurity
            .ignoring()
            .antMatchers("/index.html", "/resources/**", "/swagger/**", "/copyright*", "/api-docs/**")
        .and()
            .debug(true);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // @formatter:off
        http.anonymous().disable()
            .antMatcher("/oauth/token")
            .authorizeRequests().anyRequest().authenticated()
        .and()
            .httpBasic().authenticationEntryPoint(authenticationEntryPoint())
        .and()
            .csrf().requireCsrfProtectionMatcher(new AntPathRequestMatcher("/oauth/token")).disable()
            .exceptionHandling().accessDeniedHandler(oAuth2AccessDeniedHandler)
        .and()
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        // @formatter:on
    }

}

@Configuration
@EnableResourceServer
protected static class ResourceServerConfig extends ResourceServerConfigurerAdapter {

    @Autowired
    private ResourceServerTokenServices tokenServices;

    @Autowired
    private OAuth2AccessDeniedHandler oAuth2AccessDeniedHandler;

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        resources.tokenServices(tokenServices);
        resources.resourceId("*****");
    }


    @Override
    public void configure(HttpSecurity http) throws Exception {

        // @formatter:off
        http
            .anonymous()
            .disable();

        // API calls
        http
            .authorizeRequests()
            .regexMatchers(HttpMethod.POST, "/api/.*/accounts")
            .access("#oauth2.hasScope('trust') and #oauth2.hasScope('play') and (hasRole('ROLE_USER'))")
        .and()
        //.addFilterBefore(clientCredentialsTokenEndpointFilter(), BasicAuthenticationFilter.class)
            .sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.NEVER)
        .and()
            .exceptionHandling()
            .accessDeniedHandler(oAuth2AccessDeniedHandler);

        // API calls
        http
            .authorizeRequests()
            .antMatchers("/api/**")
            .access("#oauth2.hasScope('trust') and #oauth2.hasScope('play') and (hasRole('ROLE_USER'))")
        .and()
        //.addFilterBefore(clientCredentialsTokenEndpointFilter(), BasicAuthenticationFilter.class)
            .sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.NEVER)
        .and()
            .exceptionHandling()
            .accessDeniedHandler(oAuth2AccessDeniedHandler);
        // @formatter:on
    }


}

@Configuration
@EnableAuthorizationServer
protected static class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private DataSource dataSource;

    @Autowired
    private DefaultTokenServices tokenServices;

    @Autowired
    private ClientDetailsService clientDetailsService;

    @Autowired
    private UserApprovalHandler userApprovalHandler;

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.clientDetailsService(clientDetailsService)
        .tokenServices(tokenServices)
        .userApprovalHandler(userApprovalHandler);
    }

}
}

Adding more info on this (per Dave Syer's question)

Doesn't generate token, see below for more information.

Request:

curl -k -i -H "Accept: application/json" -X POST -d "grant_type=password&client_id=*****-php-demo&client_secret=???????&scope=play trust&username=tester&password=121212" https://localhost:8443/*****/oauth/token

Response:

HTTP/1.1 401 Unauthorized
Server: Apache-Coyote/1.1
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
Strict-Transport-Security: max-age=31536000 ; includeSubDomains
X-Frame-Options: DENY
Cache-Control: no-store
Pragma: no-cache
WWW-Authenticate: Basic realm="*****/client", error="unauthorized", error_description="An Authentication object was not found in the SecurityContext"
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Tue, 16 Sep 2014 14:05:19 GMT

{"error":"unauthorized","error_description":"An Authentication object was not found in the SecurityContext"}

Server log:

Request received for POST '/oauth/token':

org.apache.catalina.connector.RequestFacade@344dad0c

servletPath:
pathInfo:/oauth/token
headers: 
user-agent: curl/7.30.0
host: localhost:8443
accept: application/json
content-length: 145
content-type: application/x-www-form-urlencoded


Security filter chain: [
  WebAsyncManagerIntegrationFilter
  SecurityContextPersistenceFilter
  HeaderWriterFilter
  LogoutFilter
  BasicAuthenticationFilter
  RequestCacheAwareFilter
  SecurityContextHolderAwareRequestFilter
  SessionManagementFilter
  ExceptionTranslationFilter
  FilterSecurityInterceptor
]

(Update Oct 14)

Java security configuration;

    @Configuration
public class SecurityConfig {

    @Autowired
    private DataSource dataSource;

    @Value("${baseUrl}") private String baseUrl;

    @Bean
    public ClientDetailsService clientDetailsService() throws Exception {
        ClientDetailsServiceConfiguration serviceConfig = new ClientDetailsServiceConfiguration();

        serviceConfig.clientDetailsServiceConfigurer().inMemory()
            .withClient("abc")
            .secret("?????")
            .authorizedGrantTypes("password", "authorization_code", "refresh_token", "client_credentials")
            .authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
            .scopes("play", "trust")
        .and()
            .withClient("xyz")
            .authorizedGrantTypes("implicit")
            .authorities("ROLE_USER", "ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
            .scopes("play", "trust")
            .redirectUris(baseUrl + "/o2c.html")
            .autoApprove(true)
        .and()
            .withClient("zzz")
            .secret("?????????????????")
            .authorizedGrantTypes("password", "authorization_code", "refresh_token")
            .authorities("ROLE_USER", "ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
            .scopes("play", "trust")
            .redirectUris("https://www.runscope.com/oauth_tool/callback")
            .autoApprove(true);

        return serviceConfig.clientDetailsService();
    }

    @Bean
    public TokenStore tokenStore() {
        return new InMemoryTokenStore();
    }

    @Bean
    public DefaultTokenServices tokenServices() throws Exception {
        DefaultTokenServices tokenServices = new DefaultTokenServices();
        tokenServices.setAccessTokenValiditySeconds(6000);
        tokenServices.setClientDetailsService(clientDetailsService());
        tokenServices.setTokenEnhancer(new MyTokenEnhancer());
        tokenServices.setSupportRefreshToken(true);
        tokenServices.setTokenStore(tokenStore());
        return tokenServices;
    }

    @Bean
    public UserApprovalHandler userApprovalHandler() throws Exception {
        MyUserApprovalHandler handler = new MyUserApprovalHandler();
        handler.setApprovalStore(approvalStore());
        handler.setClientDetailsService(clientDetailsService());
        handler.setRequestFactory(new DefaultOAuth2RequestFactory(clientDetailsService()));
        return handler;
    }

    @Bean
    public ApprovalStore approvalStore() {
        TokenApprovalStore store = new TokenApprovalStore();
        store.setTokenStore(tokenStore());
        return store;
    }

    @Bean
    public OAuth2AccessDeniedHandler oAuth2AccessDeniedHandler() {
        return new OAuth2AccessDeniedHandler();
    }

    @Configuration
    @Order(Ordered.HIGHEST_PRECEDENCE)
    @EnableWebSecurity
    protected static class WebSecurityConfig extends WebSecurityConfigurerAdapter {

        @Autowired
        private DataSource dataSource;

        @Resource
        private PasswordEncoder passwordEncoder;

        @Autowired
        private ClientDetailsService clientDetailsService;

        @Autowired
        private OAuth2AccessDeniedHandler oAuth2AccessDeniedHandler;

        @Bean
        protected UserDetailsService clientDetailsUserService() {
            return new ClientDetailsUserDetailsService(clientDetailsService);
        }

        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {

            JdbcUserDetailsManagerConfigurer<AuthenticationManagerBuilder> jdbcUserDetail = new JdbcUserDetailsManagerConfigurer<AuthenticationManagerBuilder>();
            jdbcUserDetail.dataSource(dataSource);
            jdbcUserDetail.passwordEncoder(passwordEncoder);
            jdbcUserDetail.authoritiesByUsernameQuery("select a.username, r.role_name from account a, role r, account_role ar where a.id = ar.account_id and r.id = ar.role_id and a.username = ?");
            jdbcUserDetail.usersByUsernameQuery("select a.username, a.password, a.enabled, a.email from account a where a.username = ?");

            auth.apply(jdbcUserDetail);
        }

        @Bean(name="authenticationManager")
        @Override
        public AuthenticationManager authenticationManagerBean() throws Exception {
            AuthenticationManager am = super.authenticationManagerBean();
            return am; 
        }

        @Bean
        protected AuthenticationEntryPoint authenticationEntryPoint() {
            OAuth2AuthenticationEntryPoint entryPoint = new OAuth2AuthenticationEntryPoint();
            entryPoint.setTypeName("Basic");
            entryPoint.setRealmName("redrum/client");
            return entryPoint;
        }

        @Override
        public void configure(WebSecurity webSecurity) throws Exception {
            webSecurity
                .ignoring()
                .antMatchers("/resources/**", "/swagger/**", "/copyright*", "/api-docs/**")
            .and()
                .debug(true);
        }

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            // @formatter:off
            http.anonymous().disable()
                .antMatcher("/oauth/token")
                .authorizeRequests().anyRequest().authenticated()
            .and()
                .httpBasic().authenticationEntryPoint(authenticationEntryPoint())
            .and()
                .csrf().requireCsrfProtectionMatcher(new AntPathRequestMatcher("/oauth/token")).disable()
                .exceptionHandling().accessDeniedHandler(oAuth2AccessDeniedHandler)
            .and()
                .addFilterBefore(clientCredentialsTokenEndpointFilter(), BasicAuthenticationFilter.class)
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
            // @formatter:on
        }

        @Bean
        public ClientCredentialsTokenEndpointFilter clientCredentialsTokenEndpointFilter() throws Exception {
            ClientCredentialsTokenEndpointFilter filter = new ClientCredentialsTokenEndpointFilter();
            filter.setAuthenticationEntryPoint(authenticationEntryPoint());
            filter.setAuthenticationManager(authenticationManagerBean());
            return filter;
        }

    }

    @Configuration
    @EnableResourceServer
    protected static class ResourceServerConfig extends ResourceServerConfigurerAdapter {

        @Autowired
        private ResourceServerTokenServices tokenServices;

        @Autowired
        private OAuth2AccessDeniedHandler oAuth2AccessDeniedHandler;

        @Autowired
        private AuthenticationManager authenticationManager;

        @Override
        public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
            resources.tokenServices(tokenServices);
            resources.resourceId("xyz");
        }

        @Override
        public void configure(HttpSecurity http) throws Exception {

            // @formatter:off
            http
                .anonymous()
                .disable();

            // API calls
            http
                .authorizeRequests()
                .regexMatchers(HttpMethod.POST, "/api/.*/accounts")
                .access("#oauth2.hasScope('trust') and #oauth2.hasScope('play') and (hasRole('ROLE_USER'))")
            .and()
                //.addFilterBefore(clientCredentialsTokenEndpointFilter(), BasicAuthenticationFilter.class)
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.NEVER)
            .and()
                .exceptionHandling()
                .accessDeniedHandler(oAuth2AccessDeniedHandler);

            // API calls
            http
                .authorizeRequests()
                .antMatchers("/api/**")
                .access("#oauth2.hasScope('trust') and #oauth2.hasScope('play') and (hasRole('ROLE_USER'))")
            .and()
                //.addFilterBefore(clientCredentialsTokenEndpointFilter(), BasicAuthenticationFilter.class)
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.NEVER)
            .and()
                .exceptionHandling()
                .accessDeniedHandler(oAuth2AccessDeniedHandler);
            // @formatter:on
        }


    }

    @Configuration
    @EnableAuthorizationServer
    protected static class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

        @Autowired
        private DataSource dataSource;

        @Autowired
        private DefaultTokenServices tokenServices;

        @Autowired
        private ClientDetailsService clientDetailsService;

        @Autowired
        private UserApprovalHandler userApprovalHandler;

        @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
            endpoints.clientDetailsService(clientDetailsService)
            .tokenServices(tokenServices)
            .userApprovalHandler(userApprovalHandler);
        }

    }
}
like image 934
aug70co Avatar asked Sep 16 '14 03:09

aug70co


People also ask

Does Spring Security use OAuth2?

The Spring Security OAuth project has reached end of life and is no longer actively maintained by VMware, Inc. This project has been replaced by the OAuth2 support provided by Spring Security and Spring Authorization Server.

Is Spring Security deprecated?

Spring Security allows customizing HTTP security for features such as endpoints authorization or the authentication manager configuration by extending a WebSecurityConfigurerAdapter class. However, since recent versions, Spring deprecates this approach and encourages a component-based security configuration.

Why is WebSecurityConfigurerAdapter deprecated?

In Spring Security 5.7.0-M2 we deprecated the WebSecurityConfigurerAdapter , as we encourage users to move towards a component-based security configuration.

Should I use JWT or OAuth2?

If you want to provide an API to 3rd party clients, you must use OAuth2 also. OAuth2 is very flexible. JWT implementation is very easy and does not take long to implement. If your application needs this sort of flexibility, you should go with OAuth2.


1 Answers

Here is the solution; Hope it would be useful to somebody. It sure was not easy to come to this solution with the current state of Spring OAuth2 documentation and examples.

@Configuration
public class SecurityConfig {

@Autowired
private DataSource dataSource;

@Autowired
private ClientDetailsService clientDetailsService;

@Bean
public TokenStore tokenStore() {
    return new InMemoryTokenStore();
}

@Bean
public DefaultTokenServices tokenServices() throws Exception {
    DefaultTokenServices tokenServices = new DefaultTokenServices();
    tokenServices.setAccessTokenValiditySeconds(6000);
    tokenServices.setClientDetailsService(clientDetailsService);
    tokenServices.setTokenEnhancer(new MyTokenEnhancer());
    tokenServices.setSupportRefreshToken(true);
    tokenServices.setTokenStore(tokenStore());
    return tokenServices;
}

@Bean
public UserApprovalHandler userApprovalHandler() throws Exception {
    MyUserApprovalHandler handler = new MyUserApprovalHandler();
    handler.setApprovalStore(approvalStore());
    handler.setClientDetailsService(clientDetailsService);
    handler.setRequestFactory(new DefaultOAuth2RequestFactory(clientDetailsService));
    handler.setUseApprovalStore(true);
    return handler;
}

@Bean
public ApprovalStore approvalStore() {
    TokenApprovalStore store = new TokenApprovalStore();
    store.setTokenStore(tokenStore());
    return store;
}

@Bean
public OAuth2AccessDeniedHandler oAuth2AccessDeniedHandler() {
    return new OAuth2AccessDeniedHandler();
}

@Configuration
@Order(Ordered.HIGHEST_PRECEDENCE)
@EnableWebSecurity
protected static class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Value("${baseUrl}") 
    private String baseUrl;

    @Autowired
    private DataSource dataSource;

    @Resource
    private PasswordEncoder passwordEncoder;

    @Autowired
    private OAuth2AccessDeniedHandler oAuth2AccessDeniedHandler;

    @Bean
    public ClientDetailsService clientDetailsService() throws Exception {
        ClientDetailsServiceConfiguration serviceConfig = new ClientDetailsServiceConfiguration();

        serviceConfig.clientDetailsServiceConfigurer().inMemory()
            .withClient("***-***-****")
            .secret("???????????????????????????????")
            .authorizedGrantTypes("password", "authorization_code", "refresh_token", "client_credentials")
            .authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
            .scopes("play", "trust")
        .and()
            .withClient("***-******-**")
            .authorizedGrantTypes("implicit")
            .authorities("ROLE_USER", "ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
            .scopes("play", "trust")
            .redirectUris(baseUrl + "/o2c.html")
            .autoApprove(true)
        .and()
            .withClient("******-***********")
            .secret("???????????????????????????????????")
            .authorizedGrantTypes("password", "authorization_code", "refresh_token")
            .authorities("ROLE_USER", "ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
            .scopes("play", "trust")
            .redirectUris("https://www.runscope.com/oauth_tool/callback")
            .autoApprove(true);

        return serviceConfig.clientDetailsService();
    }

    @Bean
    UserDetailsService clientDetailsUserDetailsService() throws Exception {
        return new ClientDetailsUserDetailsService(clientDetailsService());
    }



    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {

        JdbcUserDetailsManagerConfigurer<AuthenticationManagerBuilder> jdbcUserDetail = new JdbcUserDetailsManagerConfigurer<AuthenticationManagerBuilder>();
        jdbcUserDetail.dataSource(dataSource);
        jdbcUserDetail.passwordEncoder(passwordEncoder);
        jdbcUserDetail.authoritiesByUsernameQuery("select a.username, r.role_name from account a, role r, account_role ar where a.id = ar.account_id and r.id = ar.role_id and a.username = ?");
        jdbcUserDetail.usersByUsernameQuery("select a.username, a.password, a.enabled, a.email from account a where a.username = ?");
        auth.apply(jdbcUserDetail);

        auth.userDetailsService(clientDetailsUserDetailsService());


    }

    @Bean(name="authenticationManager")
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Bean
    protected AuthenticationEntryPoint authenticationEntryPoint() {
        OAuth2AuthenticationEntryPoint entryPoint = new OAuth2AuthenticationEntryPoint();
        entryPoint.setTypeName("Basic");
        entryPoint.setRealmName("app/client");
        return entryPoint;
    }

    @Override
    public void configure(WebSecurity webSecurity) throws Exception {
        webSecurity
            .ignoring()
            .antMatchers("/resources/**", "/swagger/**", "/copyright*", "/api-docs/**")
        .and()
            .debug(true);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // @formatter:off
        http.anonymous().disable()
            .antMatcher("/oauth/token")
            .authorizeRequests().anyRequest().authenticated()
        .and()
            .httpBasic().authenticationEntryPoint(authenticationEntryPoint())
        .and()
            .csrf().requireCsrfProtectionMatcher(new AntPathRequestMatcher("/oauth/token")).disable()
            .exceptionHandling().accessDeniedHandler(oAuth2AccessDeniedHandler)
        .and()
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        // @formatter:on

        ClientCredentialsTokenEndpointFilter filter = new ClientCredentialsTokenEndpointFilter();
        filter.setAuthenticationManager(authenticationManagerBean());
        filter.afterPropertiesSet();
        http.addFilterBefore(filter, BasicAuthenticationFilter.class);
    }

}

@Configuration
@EnableResourceServer
protected static class ResourceServerConfig extends ResourceServerConfigurerAdapter {

    @Autowired
    private ResourceServerTokenServices tokenServices;

    @Autowired
    private OAuth2AccessDeniedHandler oAuth2AccessDeniedHandler;

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        //resources.tokenServices(tokenServices);
        resources.resourceId("app");
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {

        // @formatter:off
        http
            .anonymous()
            .disable();

        // API calls
        http
            .authorizeRequests()
            .regexMatchers(HttpMethod.POST, "/api/.*/accounts")
            .access("#oauth2.hasScope('trust') and #oauth2.hasScope('play') and (hasRole('ROLE_USER'))")
        .and()
            //.addFilterBefore(clientCredentialsTokenEndpointFilter(), BasicAuthenticationFilter.class)
            .sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.NEVER)
        .and()
            .exceptionHandling()
            .accessDeniedHandler(oAuth2AccessDeniedHandler);

        // API calls
        http
            .authorizeRequests()
            .antMatchers("/api/**")
            .access("#oauth2.hasScope('trust') and #oauth2.hasScope('play') and (hasRole('ROLE_USER'))")
        .and()
            //.addFilterBefore(clientCredentialsTokenEndpointFilter(), BasicAuthenticationFilter.class)
            .sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.NEVER)
        .and()
            .exceptionHandling()
            .accessDeniedHandler(oAuth2AccessDeniedHandler);
        // @formatter:on
    }

}

@Configuration
@EnableAuthorizationServer
protected static class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private DataSource dataSource;

    @Autowired
    private DefaultTokenServices tokenServices;

    @Autowired
    private ClientDetailsService clientDetailsService;

    @Autowired
    private UserApprovalHandler userApprovalHandler;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    AuthenticationEntryPoint authenticationEntryPoint;

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints
        .tokenServices(tokenServices)
        .userApprovalHandler(userApprovalHandler)
        .authenticationManager(authenticationManager);
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients)
            throws Exception {
        clients.withClientDetails(clientDetailsService);
    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer oauthServer)
            throws Exception {
        oauthServer.authenticationEntryPoint(authenticationEntryPoint)
                .realm("app/clients");
    }

}

}
like image 133
aug70co Avatar answered Oct 09 '22 02:10

aug70co