Upgrading Spring Security OAuth2 - java

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

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

Related

Spring Security: How to inject the AuthenticationManager in a controller? [duplicate]

I'm using Spring Security 3.2 and Spring 4.0.1
I'm working on converting an xml config into a Java config. When I annotate AuthenticationManager with #Autowired in my Filter, I'm getting an exception
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.security.authentication.AuthenticationManager] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}
I've tried injecting AuthenticationManagerFactoryBean but that also fails with a similar exception.
Here is the XML configuration I'm working from
<?xml version="1.0" encoding="UTF-8"?> <beans ...>
<security:authentication-manager id="authenticationManager">
<security:authentication-provider user-service-ref="userDao">
<security:password-encoder ref="passwordEncoder"/>
</security:authentication-provider>
</security:authentication-manager>
<security:http
realm="Protected API"
use-expressions="true"
auto-config="false"
create-session="stateless"
entry-point-ref="unauthorizedEntryPoint"
authentication-manager-ref="authenticationManager">
<security:access-denied-handler ref="accessDeniedHandler"/>
<security:custom-filter ref="tokenAuthenticationProcessingFilter" position="FORM_LOGIN_FILTER"/>
<security:custom-filter ref="tokenFilter" position="REMEMBER_ME_FILTER"/>
<security:intercept-url method="GET" pattern="/rest/news/**" access="hasRole('user')"/>
<security:intercept-url method="PUT" pattern="/rest/news/**" access="hasRole('admin')"/>
<security:intercept-url method="POST" pattern="/rest/news/**" access="hasRole('admin')"/>
<security:intercept-url method="DELETE" pattern="/rest/news/**" access="hasRole('admin')"/>
</security:http>
<bean class="com.unsubcentral.security.TokenAuthenticationProcessingFilter"
id="tokenAuthenticationProcessingFilter">
<constructor-arg value="/rest/user/authenticate"/>
<property name="authenticationManager" ref="authenticationManager"/>
<property name="authenticationSuccessHandler" ref="authenticationSuccessHandler"/>
<property name="authenticationFailureHandler" ref="authenticationFailureHandler"/>
</bean>
</beans>
Here is the Java Config I'm attempting
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Autowired
private PasswordEncoder passwordEncoder;
#Autowired
private AuthenticationEntryPoint authenticationEntryPoint;
#Autowired
private AccessDeniedHandler accessDeniedHandler;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint)
.accessDeniedHandler(accessDeniedHandler)
.and();
//TODO: Custom Filters
}
}
And this is the Custom Filter class. The line giving me trouble is the setter for AuthenticationManager
#Component
public class TokenAuthenticationProcessingFilter extends AbstractAuthenticationProcessingFilter {
#Autowired
public TokenAuthenticationProcessingFilter(#Value("/rest/useAuthenticationManagerr/authenticate") String defaultFilterProcessesUrl) {
super(defaultFilterProcessesUrl);
}
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
...
}
private String obtainPassword(HttpServletRequest request) {
return request.getParameter("password");
}
private String obtainUsername(HttpServletRequest request) {
return request.getParameter("username");
}
#Autowired
#Override
public void setAuthenticationManager(AuthenticationManager authenticationManager) {
super.setAuthenticationManager(authenticationManager);
}
#Autowired
#Override
public void setAuthenticationSuccessHandler(AuthenticationSuccessHandler successHandler) {
super.setAuthenticationSuccessHandler(successHandler);
}
#Autowired
#Override
public void setAuthenticationFailureHandler(AuthenticationFailureHandler failureHandler) {
super.setAuthenticationFailureHandler(failureHandler);
}
}
Override method authenticationManagerBean in WebSecurityConfigurerAdapter to expose the AuthenticationManager built using configure(AuthenticationManagerBuilder) as a Spring bean:
For example:
#Bean(name = BeanIds.AUTHENTICATION_MANAGER)
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
In addition to what Angular University said above you may want to use #Import to aggregate #Configuration classes to the other class (AuthenticationController in my case) :
#Import(SecurityConfig.class)
#RestController
public class AuthenticationController {
#Autowired
private AuthenticationManager authenticationManager;
//some logic
}
Spring doc about Aggregating #Configuration classes with #Import: link
When I #Bean'ed AuthenticationManager and #Autowired it in same class then needed to activate circular references but that is rather as for CDI.

Spring security: Authentication manager and global security config with Java config from xml config

I am using spring security 4.2.5.RELEASE and spring 4.3.16.RELEASE
My XML configuration working fine and its like below
<security:global-method-security secured-annotations="enabled" pre-post-annotations="enabled" access-decision-manager-ref="methodAccessDecisionManager">
<security:expression-handler ref="methodExpressionHandler"/>
</security:global-method-security>
<security:authentication-manager>
<security:authentication-provider user-service-ref="userDetailsService">
<security:password-encoder ref="passwordEncoder">
<security:salt-source user-property="saltSource" />
</security:password-encoder>
</security:authentication-provider>
<security:authentication-provider user-service-ref="userDetailsService">
<security:password-encoder ref="bcryptPasswordEncoder"/>
</security:authentication-provider>
</security:authentication-manager>
I have also http security configuration. need this configuration with java configuration. but can not do it because I am not finding any solution for
access-decision-manager-ref="methodAccessDecisionManager" and <security:expression-handler ref="methodExpressionHandler"/>
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.addFilterAt(switchUserProcessingFilter(), SwitchUserFilter.class)
.authorizeRequests()
.accessDecisionManager(webAccessDecisionManager())
.antMatchers("/pages/login.jsf").permitAll()
.antMatchers("/pages/expired.jsf").permitAll()
.antMatchers("/css/*").permitAll()
.antMatchers("/images/*").permitAll()
.antMatchers("/pages/testui/*").access("hasRole('PRIVILEGE_TESTER')")
.antMatchers("/pages/client/*").access("hasAnyRole('PRIVILEGE_USE_TENDERING, PRIVILEGE_MANAGE_USERS')")
.antMatchers("/pages/html5/**").access("hasAnyRole('PRIVILEGE_USE_TENDERING, PRIVILEGE_USE_SPOTREQUEST')")
.antMatchers("/moker/*").access("isAuthenticated()")
.antMatchers("/e/*").access("hasRole('PRIVILEGE_FILE')")
.and()
.formLogin()
.loginPage("/pageogin.jsf")
.usernameParameter("j_username")
.passwordParameter("j_password")
.loginProcessingUrl("/j_sy_check")
.failureUrl("/pages/l_error=1")
.successHandler(tenderEasyAuthSuccessHandler())
.and()
.exceptionHandling()
.accessDeniedHandler(accessDeniedHandler())
.and()
.logout()
.logoutUrl("/j_spring_security_logout")
.logoutSuccessUrl("/pages/logout.jsf")
.and();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService())
.passwordEncoder(passwordEncoder().isPasswordValid(encPass, rawPass, salt))
}
}
But here I am finding anything for:
access-decision-manager-ref="methodAccessDecisionManager", security:expression-handler ref=methodExpressionHandler and
security:salt-source user-property=saltSource
To specify a method expression handler and access decision manager, use a configuration based on GlobalMethodSecurityConfiguration:
#EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class MyGlobalMethodSecurityConfiguration extends GlobalMethodSecurityConfiguration {
#Autowired
private MethodSecurityExpressionHandler methodExpressionHandler;
#Autowired
private AccessDecisionManager methodAccessDecisionManager;
#Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
return methodExpressionHandler;
}
#Override
protected AccessDecisionManager accessDecisionManager() {
return methodAccessDecisionManager;
}
}
(Remove #EnableGlobalMethodSecurity from your SecurityConfig).
See also Spring Security Reference Docs.

waffle custom error page in spring

I am using waffle 1.7 + spring 4 + spring security 3.2 + thymeleaf. My problem is, that I am unable to provide custom error page when fall-back form logging fails. This is my configuration:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/**")
.authenticated()
.and()
.exceptionHandling()
.authenticationEntryPoint(negotiateSecurityFilterEntryPoint())
.accessDeniedPage("/access-denied")
.and()
.addFilterBefore(waffleNegotiateSecurityFilter(),
BasicAuthenticationFilter.class);
}
When user uses browser with SNPENGO off and enters wrong credentials, the default system 500 page appears with following information:
com.sun.jna.platform.win32.Win32Exception: The logon attempt failed. waffle.windows.auth.impl.WindowsAuthProviderImpl.acceptSecurityToken(WindowsAuthProviderImpl.java:134)
waffle.servlet.spi.NegotiateSecurityFilterProvider.doFilter(NegotiateSecurityFilterProvider.java:103) waffle.servlet.spi.SecurityFilterProviderCollection.doFilter(SecurityFilterProviderCollection.java:130)
...
How can I provide my custom page (access-denied.html thymeleaf template) ? So far I have tried everything from http://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc but without success.
Can you try creating a DelegatingNegotiateSecurityFilter and setting an AuthenticationFailureHandler.
Example of DelegatingNegotiateSecurityFilter bean configuration:
<bean id="waffleNegotiateSecurityFilter"
class="waffle.spring.DelegatingNegotiateSecurityFilter"
>
<property name="allowGuestLogin" value="false" />
<property name="Provider" ref="waffleSecurityFilterProviderCollection" />
<property name="authenticationManager" ref="authenticationManager" />
<property name="authenticationSuccessHandler" ref="authenticationSuccessHandler" />
<property name="authenticationFailureHandler" ref="authenticationFailureHandler" />
<property name="accessDeniedHandler" ref="accessDeniedHandler" />
<property name="defaultGrantedAuthority">
<null />
</property>
</bean>
The AuthenticationManager allows for the service provider to authorize the principal.
The authenticationSuccessHandler allows for the service provider to further populate the Authentication object.
The AuthenticationFailureHandler is called if the AuthenticationManager throws an AuthenticationException.
The AccessDeniedHandler is called if the AuthenticationManager throws an AccessDeniedException.
I hope this helps ...
After digging into Spring documentation and tracking what actually waffle does I have been able to solve it in the following "ugly" way. 1. disabling security for /access-denied page to prevent endless redirection loop 2. wrapping waffle filter to catch all exceptions and redirect it
Does anyone have better solution ?
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/access-denied")
.permitAll()
.and()
.authorizeRequests()
.antMatchers("/**")
.authenticated()
.and()
.exceptionHandling()
.authenticationEntryPoint(negotiateSecurityFilterEntryPoint())
.accessDeniedPage("/access-denied")
.and()
.addFilterBefore(waffleNegotiateSecurityFilter(),
BasicAuthenticationFilter.class);
}
public class WaffleWrapperSecurityBean extends GenericFilterBean {
#NotNull
private final GenericFilterBean wrappedFilter;
public WaffleWrapperSecurityBean(GenericFilterBean filter) {
wrappedFilter = filter;
}
#Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
try {
wrappedFilter.doFilter(request, response, chain);
} catch (Exception e) {
((HttpServletResponse) response)
.sendRedirect("access-denied?message="
+ e.getLocalizedMessage());
}
}
#Override
public void destroy() {
wrappedFilter.destroy();
}
}
// controller code ommited

Spring OAuth2 "Full authentication is required to access this resource"

I'm trying to use Spring OAuth2 for my rest app.
But looks like I made a mistake and I can find where I did it.
The flow should be:
1. get token from /oauth/token with username and password
2. make request to /security with provided token
MethodSecurityConfig:
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true, proxyTargetClass = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
#Autowired
private SecurityConfiguration securityConfig;
#Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
return new OAuth2MethodSecurityExpressionHandler();
}
}
OAuth2ServerConfig:
#Configuration
public class OAuth2ServerConfig {
private static final String RESOURCE_ID = "nessnity";
#Configuration
#Order(10)
protected static class UiResourceConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.requestMatchers().antMatchers("/security")
.and()
.authorizeRequests()
.antMatchers("/security").access("hasRole('USER')");
}
}
#Configuration
#EnableResourceServer
protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.resourceId(RESOURCE_ID);
}
#Override
public void configure(HttpSecurity http) throws Exception {
http
.requestMatchers().antMatchers("/security/")
.and()
.authorizeRequests()
.antMatchers("/security").access("#oauth2.hasScope('read')");
}
}
#Configuration
#EnableAuthorizationServer
protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
#Autowired
private TokenStore tokenStore;
#Autowired
private UserApprovalHandler userApprovalHandler;
#Autowired
private AuthenticationManager authenticationManager;
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("my-client")
.resourceIds(RESOURCE_ID)
.authorizedGrantTypes("client_credentials")
.authorities("ROLE_CLIENT")
.scopes("read")
.secret("password")
.accessTokenValiditySeconds(60);
}
#Bean
public TokenStore tokenStore() {
return new InMemoryTokenStore();
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.tokenStore(tokenStore)
.userApprovalHandler(userApprovalHandler)
.authenticationManager(authenticationManager);
}
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.realm("sparklr2/client");
}
}
protected static class Stuff {
#Autowired
private ClientDetailsService clientDetailsService;
#Autowired
private TokenStore tokenStore;
#Bean
public ApprovalStore approvalStore() throws Exception {
TokenApprovalStore store = new TokenApprovalStore();
store.setTokenStore(tokenStore);
return store;
}
#Bean
#Lazy
#Scope(proxyMode=ScopedProxyMode.TARGET_CLASS)
public SparklrUserApprovalHandler userApprovalHandler() throws Exception {
SparklrUserApprovalHandler handler = new SparklrUserApprovalHandler();
handler.setApprovalStore(approvalStore());
handler.setRequestFactory(new DefaultOAuth2RequestFactory(clientDetailsService));
handler.setClientDetailsService(clientDetailsService);
handler.setUseApprovalStore(true);
return handler;
}
}
}
SecurityConfiguration:
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("root")
.password("password")
.roles("USER");
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/oauth/uncache_approvals", "/oauth/cache_approvals");
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().hasRole("USER");
}
}
Problem: when I tried to get token
curl --user root:password --data "grant_type=client_credentials" http://localhost:8080/oauth/token
I got message:
{"error":"invalid_client","error_description":"Bad client
credentials"}
The second question is how to pass username/password in the url params like /oauth/token?username=root&password=password ?
Thanks.
UPDATE
I decided to start from scratch and use xml configuration.
The following configuration works perfect:
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:oauth="http://www.springframework.org/schema/security/oauth2"
xsi:schemaLocation="http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2-1.0.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.2.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
<http pattern="/oauth/token" create-session="stateless"
authentication-manager-ref="authenticationManager"
xmlns="http://www.springframework.org/schema/security">
<intercept-url pattern="/oauth/token" access="IS_AUTHENTICATED_FULLY"/>
<anonymous enabled="false"/>
<http-basic entry-point-ref="clientAuthenticationEntryPoint"/>
<custom-filter ref="clientCredentialsTokenEndpointFilter" before="BASIC_AUTH_FILTER"/>
<access-denied-handler ref="oauthAccessDeniedHandler"/>
</http>
<bean id="clientCredentialsTokenEndpointFilter"
class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter">
<property name="authenticationManager" ref="authenticationManager"/>
</bean>
<authentication-manager alias="authenticationManager"
xmlns="http://www.springframework.org/schema/security">
<authentication-provider user-service-ref="clientDetailsUserService"/>
</authentication-manager>
<bean id="clientDetailsUserService"
class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService">
<constructor-arg ref="clientDetails"/>
</bean>
<bean id="clientDetails" class="com.nessnity.api.security.OAuthClienDetailsService">
<property name="id" value="testuser"/>
<property name="secretKey" value="secret" />
</bean>
<bean id="clientAuthenticationEntryPoint"
class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
<property name="realmName" value="springsec/client"/>
<property name="typeName" value="Basic"/>
</bean>
<bean id="oauthAccessDeniedHandler"
class="org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler"/>
<oauth:authorization-server
client-details-service-ref="clientDetails"
token-services-ref="tokenServices">
<oauth:authorization-code/>
<oauth:implicit/>
<oauth:refresh-token/>
<oauth:client-credentials/>
<oauth:password authentication-manager-ref="userAuthenticationManager"/>
</oauth:authorization-server>
<authentication-manager id="userAuthenticationManager"
xmlns="http://www.springframework.org/schema/security">
<authentication-provider ref="customUserAuthenticationProvider">
</authentication-provider>
</authentication-manager>
<bean id="customUserAuthenticationProvider"
class="com.nessnity.api.security.OAuthUserAuthenticationProvider">
</bean>
<bean id="tokenServices"
class="org.springframework.security.oauth2.provider.token.DefaultTokenServices">
<property name="tokenStore" ref="tokenStore"/>
<property name="supportRefreshToken" value="true"/>
<property name="accessTokenValiditySeconds" value="900000000"/>
<property name="clientDetailsService" ref="clientDetails"/>
</bean>
<bean id="tokenStore"
class="org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore"/>
<bean id="oauthAuthenticationEntryPoint"
class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
</bean>
<http pattern="/resources/**" create-session="never"
entry-point-ref="oauthAuthenticationEntryPoint"
xmlns="http://www.springframework.org/schema/security">
<anonymous enabled="false"/>
<intercept-url pattern="/resources/**" method="GET"/>
<custom-filter ref="resourceServerFilter" before="PRE_AUTH_FILTER"/>
<access-denied-handler ref="oauthAccessDeniedHandler"/>
</http>
<oauth:resource-server id="resourceServerFilter"
resource-id="springsec" token-services-ref="tokenServices"/>
</beans>
I have faced similar for me it worked after doing the following change
In your AuthorizationServerConfiguration class replace
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.realm("sparklr2/client");
}
with the below code
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
//oauthServer.realm("sparklr2/client");
oauthServer.allowFormAuthenticationForClients();
}
and request should like
/oauth/token?grant_type=password&scope=read+write&client_id=yourclientId&client_secret=secret&username=userName&password=pwd
In your access token request you are using client credentials grant type. OAuth spec says that in case of client_credentials grant type you need to provide base64 encoded client_id:client_secret as Basic Authorization header.
For example if your client_id is google and client_secret is x23r-ss56-rfg8-6yt6, then you need to add these string as google:x23r-ss56-rfg8-6yt6, encode it using Base64 encoder and make request as
curl --header "Authorization: Basic <base64 encoded_string>" --data "grant_type=client_credentials" http://localhost:8080/oauth/token

How To Inject AuthenticationManager using Java Configuration in a Custom Filter

I'm using Spring Security 3.2 and Spring 4.0.1
I'm working on converting an xml config into a Java config. When I annotate AuthenticationManager with #Autowired in my Filter, I'm getting an exception
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.security.authentication.AuthenticationManager] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}
I've tried injecting AuthenticationManagerFactoryBean but that also fails with a similar exception.
Here is the XML configuration I'm working from
<?xml version="1.0" encoding="UTF-8"?> <beans ...>
<security:authentication-manager id="authenticationManager">
<security:authentication-provider user-service-ref="userDao">
<security:password-encoder ref="passwordEncoder"/>
</security:authentication-provider>
</security:authentication-manager>
<security:http
realm="Protected API"
use-expressions="true"
auto-config="false"
create-session="stateless"
entry-point-ref="unauthorizedEntryPoint"
authentication-manager-ref="authenticationManager">
<security:access-denied-handler ref="accessDeniedHandler"/>
<security:custom-filter ref="tokenAuthenticationProcessingFilter" position="FORM_LOGIN_FILTER"/>
<security:custom-filter ref="tokenFilter" position="REMEMBER_ME_FILTER"/>
<security:intercept-url method="GET" pattern="/rest/news/**" access="hasRole('user')"/>
<security:intercept-url method="PUT" pattern="/rest/news/**" access="hasRole('admin')"/>
<security:intercept-url method="POST" pattern="/rest/news/**" access="hasRole('admin')"/>
<security:intercept-url method="DELETE" pattern="/rest/news/**" access="hasRole('admin')"/>
</security:http>
<bean class="com.unsubcentral.security.TokenAuthenticationProcessingFilter"
id="tokenAuthenticationProcessingFilter">
<constructor-arg value="/rest/user/authenticate"/>
<property name="authenticationManager" ref="authenticationManager"/>
<property name="authenticationSuccessHandler" ref="authenticationSuccessHandler"/>
<property name="authenticationFailureHandler" ref="authenticationFailureHandler"/>
</bean>
</beans>
Here is the Java Config I'm attempting
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Autowired
private PasswordEncoder passwordEncoder;
#Autowired
private AuthenticationEntryPoint authenticationEntryPoint;
#Autowired
private AccessDeniedHandler accessDeniedHandler;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint)
.accessDeniedHandler(accessDeniedHandler)
.and();
//TODO: Custom Filters
}
}
And this is the Custom Filter class. The line giving me trouble is the setter for AuthenticationManager
#Component
public class TokenAuthenticationProcessingFilter extends AbstractAuthenticationProcessingFilter {
#Autowired
public TokenAuthenticationProcessingFilter(#Value("/rest/useAuthenticationManagerr/authenticate") String defaultFilterProcessesUrl) {
super(defaultFilterProcessesUrl);
}
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
...
}
private String obtainPassword(HttpServletRequest request) {
return request.getParameter("password");
}
private String obtainUsername(HttpServletRequest request) {
return request.getParameter("username");
}
#Autowired
#Override
public void setAuthenticationManager(AuthenticationManager authenticationManager) {
super.setAuthenticationManager(authenticationManager);
}
#Autowired
#Override
public void setAuthenticationSuccessHandler(AuthenticationSuccessHandler successHandler) {
super.setAuthenticationSuccessHandler(successHandler);
}
#Autowired
#Override
public void setAuthenticationFailureHandler(AuthenticationFailureHandler failureHandler) {
super.setAuthenticationFailureHandler(failureHandler);
}
}
Override method authenticationManagerBean in WebSecurityConfigurerAdapter to expose the AuthenticationManager built using configure(AuthenticationManagerBuilder) as a Spring bean:
For example:
#Bean(name = BeanIds.AUTHENTICATION_MANAGER)
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
In addition to what Angular University said above you may want to use #Import to aggregate #Configuration classes to the other class (AuthenticationController in my case) :
#Import(SecurityConfig.class)
#RestController
public class AuthenticationController {
#Autowired
private AuthenticationManager authenticationManager;
//some logic
}
Spring doc about Aggregating #Configuration classes with #Import: link
When I #Bean'ed AuthenticationManager and #Autowired it in same class then needed to activate circular references but that is rather as for CDI.

Categories