I have a Spring Security configuration where I want requests to /stats to be secured in a different realm than all other requests, i.e. when I navigate to / I will be prompted with a HTTP basic login form and after I login and navigate to /stats there is another HTTP basic login form since I'm not yet logged in in that realm. The problem with my current configuration is that once I'm logged in at / and navigate to /stats I get a 403 as I'm already logged in but with a different authority. Here is my configuration:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity
public class PortalServiceSecurityConfig {
#Configuration
#Order(1)
static class StatsWebSecConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("password").authorities("STATS");
}
#Bean
public AuthenticationEntryPoint authenticationEntryPoint() {
final BasicAuthenticationEntryPoint entryPoint = new BasicAuthenticationEntryPoint();
entryPoint.setRealmName("stats realm");
return entryPoint;
}
#Override
protected void configure(final HttpSecurity http) throws Exception {
http.antMatcher("/stats").authorizeRequests()
.antMatchers("/stats").hasAuthority("STATS")
.and().httpBasic().authenticationEntryPoint(authenticationEntryPoint());
}
}
#Configuration
#Order(2)
static class PortalServiceConfig extends WebSecurityConfigurerAdapter {
#Autowired
private WebserviceAuthenticationConfiguration configService;
#Autowired
private SoapFaultDetailtConverter soapFaulDetailtConverter;
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
#Override
protected UserDetailsService userDetailsService() {
return new DefaultPortalSecurityService(configService);
}
#Bean
SecurityWebApplicationInitializer securityWebApplicationInitializer() {
return new SecurityWebApplicationInitializer();
}
#Override
public void configure(final AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService());
}
#Bean
public AuthenticationEntryPoint authenticationEntryPoint() {
final BasicAuthenticationEntryPoint entryPoint = new BasicAuthenticationEntryPoint();
entryPoint.setRealmName("ws realm");
return entryPoint;
}
#Override
protected void configure(final HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/resources/**").permitAll()
.anyRequest().hasAuthority("WEBSERVICE")
.and()
.csrf().disable()
.httpBasic().authenticationEntryPoint(authenticationEntryPoint());
}
}
}
Related
I'm building a system using Spring Boot and I would like to authenticate a user in two ways:
Form authentication
Oauth2 authentication
1) I would like that users that login through Oauth2 are able to call /api/** urls but cannot access /admin/** section
2) I would like that users that login through Form are able to access /admin/** section but cannot access /api/**
In the first case the authentication will happen for example through a CURL call while on the second case through a web form.
Now configuring spring-boot I could authenticate in both ways but never they were working together. When I configure the formLogin I cannot authenticate with the Oauth2 and viceversa.
below the WebSecurityConfigurerAdapter
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Bean
public PasswordEncoder passwordEncoder() {
return new StandardPasswordEncoder();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/resources/**");
}
#Order(1)
#Override
public void configure(HttpSecurity http) throws Exception {
http
.csrf().disable().authorizeRequests()
.and()
.formLogin()
.loginProcessingUrl("/login")
.permitAll()
.and()
.logout()
.logoutUrl("/logout")
.deleteCookies("JSESSIONID")
.permitAll()
.and()
.exceptionHandling();
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#EnableGlobalMethodSecurity(prePostEnabled = true, jsr250Enabled = true)
public static class GlobalSecurityConfiguration extends GlobalMethodSecurityConfiguration {
#Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
return new OAuth2MethodSecurityExpressionHandler();
}
}
}
and the OAuth2Configuration
#Configuration
public class OAuth2Configuration {
#Order(2)
#Configuration
#EnableResourceServer
protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
private static final String RESOURCE_ID = "bancadati";
#Autowired
private CustomAuthenticationEntryPoint customAuthenticationEntryPoint;
#Autowired
private CustomLogoutSuccessHandler customLogoutSuccessHandler;
#Override
public void configure(HttpSecurity http) throws Exception {
http
.exceptionHandling()
.authenticationEntryPoint(customAuthenticationEntryPoint)
.and()
.logout()
.logoutUrl("/oauth/logout")
.logoutSuccessHandler(customLogoutSuccessHandler)
.and()
.csrf()
.requireCsrfProtectionMatcher(new AntPathRequestMatcher("/oauth/authorize"))
.disable()
.headers()
.frameOptions()
.disable()
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().authorizeRequests()
.antMatchers("/api/**").authenticated();
}
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId(RESOURCE_ID);
}
}
#Configuration
#EnableAuthorizationServer
protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter implements EnvironmentAware {
private static final String ENV_OAUTH = "authentication.oauth.";
private RelaxedPropertyResolver propertyResolver;
#Autowired
private DataSource dataSource;
#Bean
public TokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}
#Autowired
#Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
throws Exception {
endpoints
.tokenStore(tokenStore())
.authenticationManager(authenticationManager);
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(dataSource);
}
#Override
public void setEnvironment(Environment environment) {
this.propertyResolver = new RelaxedPropertyResolver(environment, ENV_OAUTH);
}
}}
I also tried to do some changes for instance I changed .authenticated() into .fullyAuthenticated() in the ResourceServerConfigurerAdapter but nothing changed. I tried also to follow this example https://github.com/rynkowsw/web-and-oauth2-security with no luck!
Can you help me to understand what I'm doing wrong?
Thank you all.
I found a solution
may be can help someone else below the code:
#Configuration
#EnableResourceServer
public class ResourceConfiguration extends ResourceServerConfigurerAdapter {
private static final String RESOURCE_ID = "resource_id";
#Override
public void configure(HttpSecurity http) throws Exception {
http
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers(HttpMethod.GET, "/api/**").access("#oauth2.hasScope('read')")
.antMatchers(HttpMethod.POST, "/api/**").access("#oauth2.hasScope('write')");
}
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId(RESOURCE_ID);
}
}
The authorization server configuration
#Configuration
public class AuthorizationConfiguration {
#Configuration
#Order(-20)
static class LoginConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Bean
public PasswordEncoder passwordEncoder() {
return new StandardPasswordEncoder();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.formLogin().loginPage("/login").permitAll()
.and().logout().logoutUrl("/logout").permitAll()
.and()
.requestMatchers()
.antMatchers("/", "/login", "/oauth/authorize", "/oauth/confirm_access")
.and()
.authorizeRequests()
.anyRequest().authenticated();
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
#Configuration
#EnableAuthorizationServer
static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter implements EnvironmentAware {
private static final String ENV_OAUTH = "authentication.oauth.";
private RelaxedPropertyResolver propertyResolver;
#Autowired
private DataSource dataSource;
#Bean
public TokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}
#Autowired
#Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
throws Exception {
endpoints
.tokenStore(tokenStore())
.authenticationManager(authenticationManager);
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(dataSource);
}
#Override
public void setEnvironment(Environment environment) {
this.propertyResolver = new RelaxedPropertyResolver(environment, ENV_OAUTH);
}
}
}
I'm developing a Spring Boot application where I'm trying to use the #PreAuthorize annotation to filter access to the User resource so that an User only can access to his own resource. Here is my UserRepository:
#Repository
#RepositoryRestResource
public interface MyUserRepository extends PagingAndSortingRepository<MyUser, UUID> {
#Override
#PreAuthorize("principal.getId().equals(#uuid)")
MyUser findOne(UUID uuid);
MyUser findByUsername(#Param("username") String username);
MyUser findByEmail(#Param("email") String email);
}
You can see the stack trace here.
Somewhere in the stacktrace references the class WebSecurityConfig line 42. This is the method configureAuthentication of the following class:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private RestAuthenticationEntryPoint unauthorizedHandler;
#Autowired
private BasicUserDetailsService userDetailsService;
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Autowired
public void configureAuthentication(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder
.userDetailsService(this.userDetailsService)
.passwordEncoder(passwordEncoder());
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public JwtAuthenticationTokenFilter authenticationTokenFilterBean() throws Exception {
JwtAuthenticationTokenFilter authenticationTokenFilter = new JwtAuthenticationTokenFilter();
authenticationTokenFilter.setAuthenticationManager(authenticationManagerBean());
return authenticationTokenFilter;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
// we don't need CSRF because our token is invulnerable
.csrf().disable()
.exceptionHandling()
.authenticationEntryPoint(unauthorizedHandler)
.and()
// don't create session
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers(HttpMethod.POST, "/login").permitAll()
.antMatchers(HttpMethod.POST, "/myUsers").permitAll()
.antMatchers(HttpMethod.PUT).authenticated()
.antMatchers(HttpMethod.POST).authenticated()
.antMatchers(HttpMethod.DELETE).authenticated()
.antMatchers(HttpMethod.PATCH).authenticated()
.anyRequest().permitAll();
// Custom JWT based security filter
http.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
http.addFilterBefore(new CORSFilter(), ChannelProcessingFilter.class);
}
}
Thank you!
Updating to Spring boot 1.4 has solved the problem.
Trying to make spring security oauth2 and form login work in a spring boot application.
I took cues from
https://github.com/spring-projects/spring-security-oauth/tree/master/samples/oauth2/sparklr
and
https://github.com/royclarkson/spring-rest-service-oauth/issues/11
Below is my configuration
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Inject
private AjaxAuthenticationSuccessHandler ajaxAuthenticationSuccessHandler;
#Inject
private AjaxAuthenticationFailureHandler ajaxAuthenticationFailureHandler;
#Inject
private AjaxLogoutSuccessHandler ajaxLogoutSuccessHandler;
#Inject
private UserDetailsService userDetailsService;
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Inject
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
#Bean
public SecurityEvaluationContextExtension securityEvaluationContextExtension() {
return new SecurityEvaluationContextExtension();
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers("/scripts/**/*.{js,html}")
.antMatchers("/bower_components/**")
.antMatchers("/i18n/**")
.antMatchers("/assets/**")
.antMatchers("/swagger-ui/index.html")
.antMatchers("/api/register")
.antMatchers("/api/activate")
.antMatchers("/api/account/reset_password/init")
.antMatchers("/api/account/reset_password/finish")
.antMatchers("/api/home/**")
.antMatchers("/api/product/**")
.antMatchers("/test/**")
.antMatchers("/devadmin/**")
.antMatchers("/signin")
.antMatchers("/static/api-guide.html");
}
#Override
#Order(Ordered.HIGHEST_PRECEDENCE)
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable().authorizeRequests().antMatchers("/web/**","/for","/admin","/here").authenticated()
.and()
.formLogin()
.loginProcessingUrl("/web/authentication")
.successHandler(ajaxAuthenticationSuccessHandler)
.failureHandler(ajaxAuthenticationFailureHandler)
.usernameParameter("j_username")
.passwordParameter("j_password")
.permitAll()
.and()
.logout()
.logoutUrl("/web/logout")
.logoutSuccessHandler(ajaxLogoutSuccessHandler)
.deleteCookies("JSESSIONID")
.permitAll()
.and()
.exceptionHandling()
;
}
#Configuration
#EnableAuthorizationServer
#EnableConfigurationProperties(SecurityConfigurationProperties.class)
protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter implements EnvironmentAware {
private static final String ENV_OAUTH = "authentication.oauth.";
private static final String PROP_CLIENTID = "clientid";
private static final String PROP_SECRET = "secret";
private static final String PROP_TOKEN_VALIDITY_SECONDS = "tokenValidityInSeconds";
private RelaxedPropertyResolver propertyResolver;
#Inject
private DataSource dataSource;
#Bean
public TokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}
#Inject
#Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
throws Exception {
endpoints
.tokenStore(tokenStore())
.authenticationManager(authenticationManager);
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
.inMemory()
.withClient(propertyResolver.getProperty(PROP_CLIENTID))
.scopes("read", "write")
.authorities(AuthoritiesConstants.ADMIN, AuthoritiesConstants.USER)
.authorizedGrantTypes("password", "refresh_token")
.secret(propertyResolver.getProperty(PROP_SECRET))
.accessTokenValiditySeconds(propertyResolver.getProperty(PROP_TOKEN_VALIDITY_SECONDS, Integer.class, 1800));
}
#Override
public void setEnvironment(Environment environment) {
this.propertyResolver = new RelaxedPropertyResolver(environment, ENV_OAUTH);
}
}
#Order(2)
#Configuration
#EnableResourceServer
protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
// #formatter:off
http
.requestMatchers().antMatchers("/api/**")
.and()
.authorizeRequests().antMatchers("/api /**").access("#oauth2.hasScope('read') and hasRole('ROLE_USER')");
// #formatter:on
}
}
}
With above configuration oauth2 login works fine when I try authenticating via
/oauth/token
However login via
/web/authentication
always shows
405 Request method 'POST' not supported
usernamepasswordauthenticationfilter is not getting invoked.
After commenting the ResourceServerConfiguration part in above code,
form login via
/web/authentication
is working fine.
Also I can see usernamepasswordauthenticationfilter being invoked from the logs.
My question is that even the end points are configured different for oauth2 resource server and form login, why oauth2 is overriding the httpsecurity for formlogin and why not usernamepasswordauthenticationfilter being called when resource server is configured?
I am trying to configure separate auth and resource servers for oauth2.
I am able to configure authrization server successfully and able to authenticate and generate access tokens. Now I want to configure a resource server which can talk to auth server with api end point to validate the access tokens.
Below is my resource server configuration.
#Configuration
#EnableResourceServer
#EnableWebSecurity
public class Oauth2SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
System.out.println("Oauth2SecurityConfiguration before");
http
.authorizeRequests()
.antMatchers(HttpMethod.GET, "/api/v1/**").authenticated();
System.out.println("Oauth2SecurityConfiguration after");
}
#Bean
public AccessTokenConverter accessTokenConverter() {
return new DefaultAccessTokenConverter();
}
#Bean
public RemoteTokenServices remoteTokenServices() {
final RemoteTokenServices remoteTokenServices = new RemoteTokenServices();
remoteTokenServices.setCheckTokenEndpointUrl("http://localhost:9000/authserver/oauth/check_token");
remoteTokenServices.setClientId("clientId");
remoteTokenServices.setClientSecret("clientSecret");
remoteTokenServices.setAccessTokenConverter(accessTokenConverter());
return remoteTokenServices;
}
#Override
#Bean
public AuthenticationManager authenticationManager() throws Exception {
OAuth2AuthenticationManager authenticationManager = new OAuth2AuthenticationManager();
authenticationManager.setTokenServices(remoteTokenServices());
return authenticationManager;
}
}
#Configuration
#EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
System.out.println("http.csrf().disable()");
http.authorizeRequests().antMatchers(HttpMethod.GET, "/api/v1/**").fullyAuthenticated();
System.out.println("http.authorizeRequests().anyRequest().authenticated()");
}
}
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true, proxyTargetClass = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
#Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
return new OAuth2MethodSecurityExpressionHandler();
}
}
Question :
1. why I AuthenticationManager at resource server while all the authentication is delegated to auth server. (I had to add it to load application context)
Apart from this I am facing below issues.
Even though I am not passing authorization headers and access token with the request. It's going through.
http GET "http://localhost:8080/DataPlatform/api/v1/123sw/members"
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Date: Mon, 19 Oct 2015 19:45:14 GMT
Server: Apache-Coyote/1.1
Transfer-Encoding: chunked
{
"entities": [],
"errors": [],
"message": null
}
The filters are only invoked at once I don't see the logs for following requests. Does it cache authorization somewhere?
I am new to spring oauth Please let me know if I am doing anything wrong. I am using
spring-security-oauth2 : 2.0.7.RELEASE
spring-security-core : 4.0.1.RELEASE
java : 1.8
The main point make separate endpoints for auth-server and resource-server, that they can services them separately, each his own.
As show bellow "/user/getEmployeesListRole/**" -access through auth-server, "/user/getEmployeesListOAuth2/**" - access through resource-server by token, which generated by aouth2-server.Also please pay attention that auth-server and oauth2-server has the same auth-manager
Configuration of spring-boot aouth2-server, resource-server, auth-server in one spring-boot app
1.Entry Point:/*AuthApplication.java*/
#SpringBootApplication
#EnableDiscoveryClient
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class AuthApplication {
public static void main(String[] args) {
SpringApplication.run(AuthApplication.class, args);
}}
2. Config of aouth2-server:/*OAuth2AuthorizationConfig.java*/
#Configuration
#EnableAuthorizationServer
public class OAuth2AuthorizationConfig extends AuthorizationServerConfigurerAdapter {
private TokenStore tokenStore = new InMemoryTokenStore();
#Autowired
#Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
#Autowired
#Qualifier("userDetailsServiceBean")
private UserDetailsService userDetailsService;
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("browser")
.authorizedGrantTypes("password", "refresh_token")
.scopes("ui", "read:ui", "write:ui");
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(tokenStore)
.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService);
}
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()")
.passwordEncoder(NoOpPasswordEncoder.getInstance());
}}
2.1 aouth2-server auth-request [post with basic auth]:http://localhost:5000/uaa/oauth/token?grant_type=password&scope=ui write:ui&username=user&password=123456&client_id=browser3.Config resource-server:
/*ResourceServer.java*/
#Configuration
#EnableResourceServer
class ResourceServer extends ResourceServerConfigurerAdapter {
//Here we specify to allow the request to the
// url /user/getEmployeesList with valid access token and scope read
#Override
public void configure(HttpSecurity http) throws Exception {
http.requestMatchers()
.antMatchers("/user/getEmployeesList/**")
.antMatchers("/user/getEmployeesListOAuth2/**")
.and().authorizeRequests().anyRequest().access("#oauth2.hasScope('ui')");
}}
4. Config auth-server:
/*WebSecurityConfig.java*/
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/resources/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/user/getEmployeesListRole/**")
.access("hasAuthority('WRITE_DATA') && hasAuthority('READ_DATA')")
.anyRequest().permitAll()
.and().formLogin().permitAll()
.and().logout().permitAll()
.and().csrf().disable();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("admin")
.password("admin")
.authorities("WRITE_DATA", "READ_DATA");
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
#Bean
public UserDetailsService userDetailsServiceBean() throws Exception {
return super.userDetailsServiceBean();
}
}
You don't need #EnableWebSecurity on Oauth2SecurityConfiguration #EnableResourceServer is enough.
You should also replace extends WebSecurityConfigurerAdapter with extends ResourceServerConfigurerAdapter.
If you want to use your RemoteTokenServices instance I recommend you override ResourceServerConfigurerAdapter public void configure(ResourceServerSecurityConfigurer resources) throws Exception with
#Override
public void configure( ResourceServerSecurityConfigurer resources ) throws Exception
{
resources.tokenServices( serverConfig.getTokenServices() );
}
I'm in the process of building separate resource server and authhorization server. For now i'm using the user-info-uri in resource server to extract the Principal from the authorization server matching the access-token, with config:
spring:
oauth2:
resource:
userInfoUri: http://localhost:9999/uaa/user
In the resource server I have protected endpoints, based on role, as follows:
http
.authorizeRequests()
.antMatchers("/invoices/**").hasRole("END_USER")
.anyRequest().authenticated();
When I manually access the user-info-uri, I can see that the the authorities contain:
"authority": "ROLE_END_USER"
But when I try to access the /invoices resource I get an Access-Denied exception, and in the log I see:
OAuth2Authentication#bc5074a8: Principal: my-login; Credentials: [PROTECTED]; Authenticated: true; Details: remoteAddress=0:0:0:0:0:0:0:1, tokenType=BearertokenValue=<TOKEN>; Granted Authorities: ROLE_USER
Authoriteis = "ROLE_USER". Where does that come from, should'nt it be "ROLE_END_USER" at this point also?
I've seen implementations using a shared database for the storage of tokens, is that really necessary for what I want to achive?
Resource Server:
#SpringBootApplication
#EnableOAuth2Resource
public class EndUserResourceServiceApplication extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/invoices/**").hasRole("END_USER")
.anyRequest().authenticated();
}
public static void main(String[] args) {
SpringApplication.run(EndUserResourceServiceApplication.class, args);
}
}
Auth Server:
#SpringBootApplication
#RestController
#EnableResourceServer
public class ApiAuthServerApplication extends ResourceServerConfigurerAdapter {
#Configuration
#EnableWebSecurity
#Order(-10)
protected static class LoginConfig extends WebSecurityConfigurerAdapter {
#Autowired
private CustomUserDetailsService userDetailsService;
#Bean(name = "authenticationManagerBean")
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
#Autowired
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.formLogin().permitAll()
.and()
.requestMatchers().antMatchers("/login", "/oauth/authorize", "/oauth/confirm_access")
.and()
.authorizeRequests().anyRequest().authenticated();
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
#Configuration
#EnableAuthorizationServer
protected static class OAuth2Config extends AuthorizationServerConfigurerAdapter {
#Autowired
private TokenStore tokenStore;
#Autowired
#Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
#Autowired
private CustomUserDetailsService userDetailsService;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.authenticationManager(this.authenticationManager)
.userDetailsService(this.userDetailsService)
.tokenStore(this.tokenStore);
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("my-client")
.secret("our_s3cret")
.authorities("ROLE_CLIENT")
.authorizedGrantTypes("implicit", "password", "refresh_token")
.redirectUris("http://anywhere")
.scopes("read")
.autoApprove(true);
}
#Bean
public TokenStore tokenStore() {
return new InMemoryTokenStore();
}
}
public static void main(String[] args) {
SpringApplication.run(ApiAuthServerApplication.class, args);
}
#RequestMapping("/user")
public Principal user(Principal user) {
return user;
}
}
Summary:
Can I use user-info-uri for validating access-tokens and to use "hasRole"
Is it necessary to use shared database for token storage when using separate resource server and authorization server?
I ended up using a shared token store, which works well at the moment