I have a server application with two components:
a) A set of REST API that are secured using oAuth ( Spring security oAuth)
b) A dashboard for management with role based UI
For business reasons, these two components need to be co-hosted i.e deployed as a single war.
Till now we only had oAuth for our REST API's and all was well.
The problem started when we tried to use form based authentication for the dashboard. Now, when we access REST API's without an oAuth token, it just redirects to the login page instead of giving a 401 unauthorized error.
Our configuration is like this:
Form based authentication(WebSecurityConfigurerAdapter):
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/css/**","/img/**","/login/**","/oauth/**).permitAll()
.anyRequest().authenticated()
.and()
.formLogin().loginPage("/login")
.defaultSuccessUrl("/delegate/success", true)
.failureUrl("/login/fail")
.permitAll()
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/login")
.permitAll();
oAuth:
Resource provider configuration:
http
.authorizeRequests()
.antMatchers("/abc").access("#oauth2.hasScope('read')and hasRole('ROLE_USER')")
.antMatchers("/xyz").access("#oauth2.hasScope('read') and hasRole('ROLE_ADMIN')")
We would basically like to configure different API to be secured differently. REST API consumed by client applications have to be secured by oAuth and Spring MVC API rendering dashboard pages with form based authentication.
Is this even possible?
EDIT:
Add ordering and able to get 401 unauthorized message on accessing oauth protected REST APIs. Form login doesnt work though. I am able to access all dashboard pages without login.
More code snippets:
#Configuration
#EnableResourceServer
public class ResourceProviderConfiguration extends ResourceServerConfigurerAdapter {
.....
#Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/abc").access("#oauth2.hasScope('read')and hasRole('ROLE_USER')")
.antMatchers("/xyz").access("#oauth2.hasScope('read') and hasRole('ROLE_ADMIN')").
.and()
.requestMatchers()
.antMatchers("/abc","/xyz","/others");
}
}
#Configuration
#EnableAuthorizationServer
public class AuthorizationProviderConfiguration extends AuthorizationServerConfigurerAdapter {
#Autowired
private TokenStore tokenStore;
#Autowired
private UserApprovalHandler userApprovalHandler;
#Autowired
ClientDetailsService webClientDetailsService;
#Autowired
#Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
.......
#Bean
public TokenStore tokenStore() {
return new InMemoryTokenStore();
}
......
}
#Configuration
#EnableWebSecurity
#Order(5)
public class UserAuthenticationConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/css/**","/img/**","/login/**",
"/oauth/**).permitAll()
.anyRequest().authenticated()
.and()
.formLogin().loginPage("/login")
.defaultSuccessUrl("/delegate/success", true)
.failureUrl("/login/fail")
.permitAll()
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/login")
.permitAll();
}
}
Spring security logs:
DEBUG o.s.s.w.u.matcher.OrRequestMatcher - Trying to match using Ant [pattern='/oauth/token']
DEBUG o.s.s.w.u.m.AntPathRequestMatcher - Checking match of request : '/def'; against '/oauth/token'
DEBUG o.s.s.w.u.matcher.OrRequestMatcher - Trying to match using Ant [pattern='/oauth/token_key']
DEBUG o.s.s.w.u.m.AntPathRequestMatcher - Checking match of request : '/def'; against '/oauth/token_key'
DEBUG o.s.s.w.u.matcher.OrRequestMatcher - Trying to match using Ant [pattern='/oauth/check_token']
DEBUG o.s.s.w.u.m.AntPathRequestMatcher - Checking match of request : '/def'; against '/oauth/check_token'
DEBUG o.s.s.w.u.matcher.OrRequestMatcher - No matches found
DEBUG o.s.s.w.u.matcher.OrRequestMatcher - Trying to match using Ant [pattern='/abc/**']
DEBUG o.s.s.w.u.m.AntPathRequestMatcher - Checking match of request : '/def'; against '/abc/**'
DEBUG o.s.s.w.u.matcher.OrRequestMatcher - Trying to match using Ant [pattern='/xyz/**']
DEBUG o.s.s.w.u.m.AntPathRequestMatcher - Checking match of request : '/def'; against '/xyz/**'
DEBUG o.s.s.w.u.matcher.OrRequestMatcher - No matches found
DEBUG o.s.security.web.FilterChainProxy - '/def' at position 1 of 11 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
DEBUG o.s.security.web.FilterChainProxy - '/def' at position 2 of 11 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
DEBUG o.s.s.w.c.HttpSessionSecurityContextRepository - HttpSession returned null object for SPRING_SECURITY_CONTEXT
DEBUG o.s.s.w.c.HttpSessionSecurityContextRepository - No SecurityContext was available from the HttpSession: org.apache.catalina.session.StandardSessionFacade#7f8059. A new one will be created.
DEBUG o.s.security.web.FilterChainProxy - '/def' at position 3 of 11 in additional filter chain; firing Filter: 'HeaderWriterFilter'
DEBUG o.s.s.w.h.writers.HstsHeaderWriter - Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher#cebda04
DEBUG o.s.security.web.FilterChainProxy - '/def' at position 4 of 11 in additional filter chain; firing Filter: 'LogoutFilter'
DEBUG o.s.s.w.u.m.AntPathRequestMatcher - Checking match of request : '/def'; against '/logout'
DEBUG o.s.security.web.FilterChainProxy - '/def' at position 5 of 11 in additional filter chain; firing Filter: 'UsernamePasswordAuthenticationFilter'
DEBUG o.s.s.w.u.m.AntPathRequestMatcher - Request 'GET '/def'' doesn't match 'POST /login/new
DEBUG o.s.security.web.FilterChainProxy - '/def' at position 6 of 11 in additional filter chain; firing Filter: 'RequestCacheAwareFilter'
DEBUG o.s.security.web.FilterChainProxy - '/def' at position 7 of 11 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
DEBUG o.s.security.web.FilterChainProxy - '/def' at position 8 of 11 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter'
DEBUG o.s.s.w.a.AnonymousAuthenticationFilter - Populated SecurityContextHolder with anonymous token: 'org.springframework.security.authentication.AnonymousAuthenticationToken#905571d8: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails#0: RemoteIpAddress: 127.0.0.1; SessionId: 794828541EF505314237BBC81C2ACAF4; Granted Authorities: ROLE_ANONYMOUS'
DEBUG o.s.security.web.FilterChainProxy - '/def' at position 9 of 11 in additional filter chain; firing Filter: 'SessionManagementFilter'
DEBUG o.s.security.web.FilterChainProxy - '/def' at position 10 of 11 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'
DEBUG o.s.security.web.FilterChainProxy - '/def' at position 11 of 11 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
DEBUG o.s.s.w.u.m.AntPathRequestMatcher - Checking match of request : '/def'; against '/css/**'
DEBUG o.s.s.w.u.m.AntPathRequestMatcher - Checking match of request : '/def'; against '/oauth/**'
DEBUG o.s.s.w.u.m.AntPathRequestMatcher - Checking match of request : '/def'; against '/img/**'
DEBUG o.s.s.w.u.m.AntPathRequestMatcher - Checking match of request : '/def'; against '/login/**'
DEBUG o.s.s.w.a.i.FilterSecurityInterceptor - Public object - authentication not attempted
DEBUG o.s.security.web.FilterChainProxy - '/def' reached end of additional filter chain; proceeding with original chain
DEBUG o.s.web.servlet.DispatcherServlet - DispatcherServlet with name 'webservice' processing GET request for ['/handler/def']
Final Configuration that worked:
1. Adding #Order to the web security configurer, after the resource provider configuration which defaults to 3.
Ensuring that WebSecurityConfigurerAdapter has .anyRequest().authenticated() config
#Configuration
#EnableResourceServer
public class ResourceProviderConfiguration extends ResourceServerConfigurerAdapter {
.....
#Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/abc").access("#oauth2.hasScope('read')and hasRole('ROLE_USER')")
.antMatchers("/xyz").access("#oauth2.hasScope('read') and hasRole('ROLE_ADMIN')").
.and()
.requestMatchers()
.antMatchers("/abc","/xyz","/others");
}
}
#Configuration
#EnableAuthorizationServer
public class AuthorizationProviderConfiguration extends AuthorizationServerConfigurerAdapter {
#Autowired
private TokenStore tokenStore;
#Autowired
private UserApprovalHandler userApprovalHandler;
#Autowired
ClientDetailsService webClientDetailsService;
#Autowired
#Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
.......
#Bean
public TokenStore tokenStore() {
return new InMemoryTokenStore();
}
......
}
#Configuration
#EnableWebSecurity
#Order(5)
public class UserAuthenticationConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/css/**","/img/**","/login/**",
"/oauth/**).permitAll()
.anyRequest().authenticated()
.and()
.formLogin().loginPage("/login")
.defaultSuccessUrl("/delegate/success", true)
.failureUrl("/login/fail")
.permitAll()
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/login")
.permitAll();
}
}
I can see it checking your permitAll() matchers in the logs, but there's no sign of the anyRequest().authenticated(). You need to add a request matcher to the HttpSecurity as well (i.e. http.requestMatchers().anyRequest()).
Related
I was trying to integrate oauth2 based token having client credential grant type with spring session based authentication. It's working fine with oauth token and the authorities given.
It's not working when I have combined them both. It always calls UsernamePasswordAuthenticationFilter and not OAuth2AuthenticationProcessingFilter
How to make them work together? Here is my ResourceServer configuration
#Configuration
#EnableResourceServer
protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.resourceId(SPARKLR_RESOURCE_ID).stateless(false);
}
#Override
public void configure(HttpSecurity http) throws Exception {
// #formatter:off
http
// Since we want the protected resources to be accessible in the UI as well we need
// session creation to be allowed (it's disabled by default in 2.0.6)
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.and()
.requestMatchers().antMatchers("/api/account/**", "/oauth/users/**", "/oauth/clients/**","/me")
.and()
.authorizeRequests()
.antMatchers("/api/account/**").access("#oauth2.hasScope('read') or (!#oauth2.isOAuth() and hasRole('ROLE_USER'))")
.regexMatchers(HttpMethod.DELETE, "/oauth/users/([^/].*?)/tokens/.*")
.access("#oauth2.clientHasRole('ROLE_CLIENT') and (hasRole('ROLE_USER') or #oauth2.isClient()) and #oauth2.hasScope('write')")
.regexMatchers(HttpMethod.GET, "/oauth/clients/([^/].*?)/users/.*")
.access("#oauth2.clientHasRole('ROLE_CLIENT') and (hasRole('ROLE_USER') or #oauth2.isClient()) and #oauth2.hasScope('read')")
.regexMatchers(HttpMethod.GET, "/oauth/clients/.*")
.access("#oauth2.clientHasRole('ROLE_CLIENT') and #oauth2.isClient() and #oauth2.hasScope('read')");
// #formatter:on
}
}
The issue is, in filter chain OAuth2AuthenticationProcessingFilter is not getting called. So the token validation is not happening for any rest call. Below is the filter chain.
XNIO-2 task-1] o.s.security.web.FilterChainProxy : /api/account at position 1 of 12 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
XNIO-2 task-1] o.s.security.web.FilterChainProxy : /api/account at position 2 of 12 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
XNIO-2 task-1] w.c.HttpSessionSecurityContextRepository : No HttpSession currently exists
XNIO-2 task-1] w.c.HttpSessionSecurityContextRepository : No SecurityContext was available from the HttpSession: null. A new one will be created.
XNIO-2 task-1] o.s.security.web.FilterChainProxy : /api/account at position 3 of 12 in additional filter chain; firing Filter: 'HeaderWriterFilter'
XNIO-2 task-1] o.s.s.w.header.writers.HstsHeaderWriter : Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher#74d294b6
XNIO-2 task-1] o.s.security.web.FilterChainProxy : /api/account at position 4 of 12 in additional filter chain; firing Filter: 'LogoutFilter'
XNIO-2 task-1] o.s.s.web.util.matcher.OrRequestMatcher : Trying to match using Ant [pattern='/api/logout', GET]
XNIO-2 task-1] o.s.s.web.util.matcher.OrRequestMatcher : No matches found
XNIO-2 task-1] o.s.security.web.FilterChainProxy : /api/account at position 5 of 12 in additional filter chain; firing Filter: 'UsernamePasswordAuthenticationFilter'
XNIO-2 task-1] o.s.s.w.u.matcher.AntPathRequestMatcher : Request 'GET /api/account' doesn't match 'POST /api/authentication
XNIO-2 task-1] o.s.security.web.FilterChainProxy : /api/account at position 6 of 12 in additional filter chain; firing Filter: 'DefaultLoginPageGeneratingFilter'
XNIO-2 task-1] o.s.security.web.FilterChainProxy : /api/account at position 7 of 12 in additional filter chain; firing Filter: 'RequestCacheAwareFilter'
XNIO-2 task-1] o.s.security.web.FilterChainProxy : /api/account at position 8 of 12 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
XNIO-2 task-1] o.s.security.web.FilterChainProxy : /api/account at position 9 of 12 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter'
XNIO-2 task-1] o.s.s.w.a.AnonymousAuthenticationFilter : Populated SecurityContextHolder with anonymous token: 'org.springframework.security.authentication.AnonymousAuthenticationToken#9055c2bc: Principal: anonymousUser; Credentials
XNIO-2 task-1] o.s.security.web.FilterChainProxy : /api/account at position 10 of 12 in additional filter chain; firing Filter: 'SessionManagementFilter'
XNIO-2 task-1] o.s.security.web.FilterChainProxy : /api/account at position 11 of 12 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'
XNIO-2 task-1] o.s.security.web.FilterChainProxy : /api/account at position 12 of 12 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
Edit:
I'm trying to merge these 2 projects together. https://github.com/jhipster/jhipster-sample-app and https://github.com/spring-projects/spring-security-oauth/tree/master/samples/oauth2/sparklr
You are using spring boot 1.5 so your resource server filter chain by default has a higher order than the custom filter chain that jhipster added. Either you need to change the orders, or change the pattern matchers so that the OAuth resources are not matched by the main filter chain. The spring boot user guide recommends that you put a custom filter chain in at a specific order (SecurityProperties.ACCESS_OVERRIDE_ORDER). Probably a good a idea to follow that advice.
I need Spring to return unauthorized (status 401) instead of redirecting user to login page. Reason is that I'm doing a SPA application backend that already has built in mechanism for redirecting when http status code 401 recieved. Version of Spring Boot I'm working with is 1.3.0 but I've also tried it with 1.4.1 and still getting the same problem.
My security configuration is:
#Configuration
#EnableWebSecurity
#EnableRedisHttpSession
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
AuthenticationProvider authenticationProvider;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/loginPath").permitAll()
.anyRequest().authenticated()
.and()
.exceptionHandling().authenticationEntryPoint(
(request, response, authException) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED))
.and()
.formLogin().loginPage("/loginPath")
.successHandler(loginSuccessHandler()).permitAll()
.and()
.logout().deleteCookies("SESSION").permitAll()
.and()
.authenticationProvider(authenticationProvider)
.csrf().disable()
.httpBasic().disable();
}
...
}
Thanks for helping.
EDIT
There was a view controller mapping for "/error" to redirect user to login page with highest priority assigned. When that was removed, everything worked as expected.
It seems like Spring Security would return 401 and /error as a view but due to redirect config and higher priority it would get overriden.
I've been working with Spring Security for two weeks now and it's working well except for anonymous users and session timeouts.
Use Case #1
An anonymous user can visit the site and view public pages (/home, /about, /signup, etc.) for as long as they want (no session timeouts).
If the user selects a protected page the login screen appears.
Use Case #2
A registered user logs in and can view protected pages.
If their session times out, the invalidSessionUrl page is displayed and the user is directed to log in.
I've read a ton of SO and blog posts but I can't seem to find the solution to Use Case #1. I'm using Spring 4.1.6.RELEASE, Spring Security 4.0.2 RELEASE and Tomcat8.
Here is my security config:
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/", "/home", "/about", "/login**", "/thankyou", "/user/signup**", "/errors/**").permitAll()
.regexMatchers("/user/signup/.*").permitAll()
.antMatchers("/recipe/listRecipes*").hasAuthority("GUEST")
.antMatchers("/recipe/addRecipe*").hasAuthority("AUTHOR")
.antMatchers("/admin/**").hasAuthority("ADMIN")
.anyRequest().authenticated()
.expressionHandler(secExpressionHandler())
.and()
.formLogin()
.loginPage("/login")
.failureUrl("/login?err=1")
.permitAll()
.and()
.logout()
.logoutSuccessUrl("/thankyou")
.deleteCookies( "JSESSIONID" )
.invalidateHttpSession(false)
.and()
.exceptionHandling()
.accessDeniedPage("/errors/403")
.and()
.rememberMe()
.key("recipeOrganizer")
.tokenRepository(persistentTokenRepository())
.tokenValiditySeconds(960) //.tokenValiditySeconds(1209600)
.rememberMeParameter("rememberMe");
http
.sessionManagement()
.sessionAuthenticationErrorUrl("/errors/402")
.invalidSessionUrl("/errors/invalidSession")
.maximumSessions(1)
.maxSessionsPreventsLogin(true)
.expiredUrl("/errors/expiredSession")
.and()
.sessionFixation().migrateSession();
Any action by an anonymous user after the session expires results in an invalid session exception. The problem is in the security filter chain (see log below) the AnonymousAuthenticationFilter (#11) creates a new session, but the SessionManagementFilter (#12) retrieves the prior expired session, compares it to the new one and throws the invalidsession exception. I want this to happen for logged in users, but not for anonymous users. However, at the time the exception is thrown the security context has been destroyed so I can't know whether the prior session was from an anonymous or logged in user.
Solutions I've considered are:
Set the global session timeout to 24 hours or more then adjust the timeout for registered users in a LoginSuccessHandler and RememberMeSuccessHandler.
Turn off security for the public pages.
Create a separate cookie to indicate the type of user (anon vs. logged in), and query that in the InvalidSessionStrategy to redirect anon users to the /home page.
Create a custom filter that is executed first to identify an anon user (possible?) and simply extend the current session.
Write some javascript or jquery to periodically check the session timeout and reset it for anon users.
Solution #1 may result in issues with expired sessions on the server (not sure if this is a big deal). #2 doesn't feel right to me, especially if a public page may include any info about a logged in user. #3 might work except that the anon user could click on a public link other than the home page but get redirected to the home page because I don't think there's a way to tell in the InvalidSessionStrategy which link they were trying to access? I'm not sure if #4 will work or not - haven't tried it yet. #5 might also work but it increases network traffic?
I'm hoping that someone can point me to a practical solution. This has to be something that many sites deal with but I'm going around in circles trying to solve. Thanks in advance for any advice or tips.
Here's a portion of a log to illustrate what happens. I've set the session timeout to 60 seconds for testing purposes.
Initial access to website
20:49:29.823 DEBUG: org.springframework.security.web.FilterChainProxy - / at position 11 of 14 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter'
20:49:29.839 DEBUG: org.springframework.security.web.authentication.AnonymousAuthenticationFilter - Populated SecurityContextHolder with anonymous token: 'org.springframework.security.authentication.AnonymousAuthenticationToken#9055c2bc: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails#b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS'
20:49:29.839 DEBUG: org.springframework.security.web.FilterChainProxy - / at position 12 of 14 in additional filter chain; firing Filter: 'SessionManagementFilter'
20:49:29.839 DEBUG: org.springframework.security.web.FilterChainProxy - / at position 13 of 14 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'
20:49:29.839 DEBUG: org.springframework.security.web.FilterChainProxy - / at position 14 of 14 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
20:49:29.839 DEBUG: org.springframework.security.web.util.matcher.AntPathRequestMatcher - Checking match of request : '/'; against '/'
20:49:29.839 DEBUG: org.springframework.security.web.access.intercept.FilterSecurityInterceptor - Secure object: FilterInvocation: URL: /; Attributes: [permitAll]
20:49:29.854 DEBUG: org.springframework.security.web.access.intercept.FilterSecurityInterceptor - Previously Authenticated: org.springframework.security.authentication.AnonymousAuthenticationToken#9055c2bc: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails#b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS
20:49:29.871 DEBUG: org.springframework.security.access.vote.AffirmativeBased - Voter: org.springframework.security.web.access.expression.WebExpressionVoter#ff577, returned: 1
20:49:29.871 DEBUG: org.springframework.security.web.access.intercept.FilterSecurityInterceptor - Authorization successful
20:49:30.136 DEBUG: net.mycompany.myapp.config.SessionListener - AuthenticationSuccess - principal: anonymousUser
20:49:30.167 DEBUG: net.mycompany.myapp.config.SessionListener - AuthenticationSuccess - authority contains: ROLE_ANONYMOUS
20:49:30.167 INFO : net.mycompany.myapp.config.SessionListener - sessionCreated: Session created on: Mon Sep 21 20:49:30 CDT 2015
20:49:30.167 INFO : net.mycompany.myapp.config.SessionListener - sessionCreated: Session last accessed on: Mon Sep 21 20:49:30 CDT 2015
20:49:30.167 INFO : net.mycompany.myapp.config.SessionListener - sessionCreated: Session expires after: 60 seconds
20:49:30.167 INFO : net.mycompany.myapp.config.SessionListener - sessionCreated: Session ID: CA34FE74B56B8EF94181B1231A7D4FF6
Session times out after 60 seconds
20:50:46.015 INFO : net.mycompany.myapp.config.SessionDestroyedListener - destroyedEvent
20:50:46.015 INFO : net.mycompany.myapp.config.SessionDestroyedListener - object:org.springframework.security.web.session.HttpSessionDestroyedEvent[source=org.apache.catalina.session.StandardSessionFacade#17827f3e]
20:50:46.015 INFO : net.mycompany.myapp.config.SessionListener - sessionDestroyed: Session created on: Mon Sep 21 20:49:30 CDT 2015
20:50:46.015 INFO : net.mycompany.myapp.config.SessionListener - sessionDestroyed: Session last accessed on: Mon Sep 21 20:49:32 CDT 2015
20:50:46.015 INFO : net.mycompany.myapp.config.SessionListener - sessionDestroyed: Session expires after: 60 seconds
20:50:46.015 INFO : net.mycompany.myapp.config.SessionListener - sessionDestroyed: Session ID: CA34FE74B56B8EF94181B1231A7D4FF6
Click on "/home" link (unsecured)
20:50:53.927 DEBUG: org.springframework.security.web.util.matcher.AntPathRequestMatcher - Checking match of request : '/home'; against '/resources/**'
20:50:53.927 DEBUG: org.springframework.security.web.FilterChainProxy - /home at position 1 of 14 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
20:50:53.927 DEBUG: org.springframework.security.web.FilterChainProxy - /home at position 2 of 14 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
20:50:53.927 DEBUG: org.springframework.security.web.context.HttpSessionSecurityContextRepository - No HttpSession currently exists
20:50:53.927 DEBUG: org.springframework.security.web.context.HttpSessionSecurityContextRepository - No SecurityContext was available from the HttpSession: null. A new one will be created.
20:50:53.927 DEBUG: org.springframework.security.web.FilterChainProxy - /home at position 3 of 14 in additional filter chain; firing Filter: 'HeaderWriterFilter'
20:50:53.927 DEBUG: org.springframework.security.web.header.writers.HstsHeaderWriter - Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher#4c668dec
20:50:53.927 DEBUG: org.springframework.security.web.FilterChainProxy - /home at position 4 of 14 in additional filter chain; firing Filter: 'CsrfFilter'
20:50:53.927 DEBUG: org.springframework.security.web.FilterChainProxy - /home at position 5 of 14 in additional filter chain; firing Filter: 'LogoutFilter'
20:50:53.927 DEBUG: org.springframework.security.web.util.matcher.AntPathRequestMatcher - Request 'GET /home' doesn't match 'POST /logout
20:50:53.927 DEBUG: org.springframework.security.web.FilterChainProxy - /home at position 6 of 14 in additional filter chain; firing Filter: 'UsernamePasswordAuthenticationFilter'
20:50:53.927 DEBUG: org.springframework.security.web.util.matcher.AntPathRequestMatcher - Request 'GET /home' doesn't match 'POST /login
20:50:53.927 DEBUG: org.springframework.security.web.FilterChainProxy - /home at position 7 of 14 in additional filter chain; firing Filter: 'ConcurrentSessionFilter'
20:50:53.927 DEBUG: org.springframework.security.web.FilterChainProxy - /home at position 8 of 14 in additional filter chain; firing Filter: 'RequestCacheAwareFilter'
20:50:53.927 DEBUG: org.springframework.security.web.FilterChainProxy - /home at position 9 of 14 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
20:50:53.927 DEBUG: org.springframework.security.web.FilterChainProxy - /home at position 10 of 14 in additional filter chain; firing Filter: 'RememberMeAuthenticationFilter'
20:50:53.927 DEBUG: org.springframework.security.web.FilterChainProxy - /home at position 11 of 14 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter'
20:50:53.927 DEBUG: org.springframework.security.web.authentication.AnonymousAuthenticationFilter - Populated SecurityContextHolder with anonymous token: 'org.springframework.security.authentication.AnonymousAuthenticationToken#9055c2bc: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails#b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS'
20:50:53.927 DEBUG: org.springframework.security.web.FilterChainProxy - /home at position 12 of 14 in additional filter chain; firing Filter: 'SessionManagementFilter'
20:50:53.927 DEBUG: org.springframework.security.web.session.SessionManagementFilter - Requested session ID CA34FE74B56B8EF94181B1231A7D4FF6 is invalid.
20:50:53.927 DEBUG: org.springframework.security.web.session.SimpleRedirectInvalidSessionStrategy - Starting new session (if required) and redirecting to '/errors/invalidSession'
20:50:53.927 DEBUG: net.mycompany.myapp.config.SessionListener - AuthenticationSuccess - principal: anonymousUser
20:50:53.927 DEBUG: net.mycompany.myapp.config.SessionListener - AuthenticationSuccess - authority contains: ROLE_ANONYMOUS
20:50:53.927 INFO : net.mycompany.myapp.config.SessionListener - sessionCreated: Session created on: Mon Sep 21 20:50:53 CDT 2015
20:50:53.927 INFO : net.mycompany.myapp.config.SessionListener - sessionCreated: Session last accessed on: Mon Sep 21 20:50:53 CDT 2015
20:50:53.942 INFO : net.mycompany.myapp.config.SessionListener - sessionCreated: Session expires after: 60 seconds
20:50:53.942 INFO : net.mycompany.myapp.config.SessionListener - sessionCreated: Session ID: 6F7FD6230BC075B7768648BBBC08E3F4
20:50:53.942 DEBUG: org.springframework.security.web.session.HttpSessionEventPublisher - Publishing event: org.springframework.security.web.session.HttpSessionCreatedEvent[source=org.apache.catalina.session.StandardSessionFacade#412cbe09]
20:50:53.942 DEBUG: org.springframework.security.web.DefaultRedirectStrategy - Redirecting to '/myapp/errors/invalidSession'
I posted a similar question a few days after this one: Why does anonymous user get redirected to expiredsessionurl by Spring Security, for which I've posted an answer. Solution #2 above works, but I don't like turning off all security even for public pages. What I ended up implementing was a combination of #3 and #4. See the linked question for the full answer.
This is a old question but I have a similar problem even now (Spring Security 5.5.1)
I found some answers but they seemed too fragile for me:
Why does anonymous user get redirected to expiredsessionurl by Spring Security (uses Cookie to detect)
Spring Security getting the acess attributes of patterns in intercept-url (using custom url interceptor)
This is what works for me (using a custom filter for Spring Security)
/**
* https://doanduyhai.wordpress.com/2012/04/21/spring-security-part-vi-session-timeout-handling-for-ajax-calls/
* 1. detect session expiry and ajax request --> send custom error code so that
* client side can detect and handle as necessary if not, it will be a redirect
* to login page and with 200 response
*
* https://forum.primefaces.org/viewtopic.php?t=16735 (use xml partial response for redirect)
* #author RadianceOng
*/
public class AjaxTimeoutRedirectFilter extends GenericFilterBean {
static Logger lg = Logger.getLogger(AjaxTimeoutRedirectFilter.class);
private ThrowableAnalyzer throwableAnalyzer = new DefaultThrowableAnalyzer();
private AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl();
String invalidSessionUrl;
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
try {
chain.doFilter(request, response);
} catch (IOException ex) {
throw ex;
} catch (Exception ex) {
Throwable[] causeChain = throwableAnalyzer.determineCauseChain(ex);
Exception ase = (AuthenticationException) throwableAnalyzer.getFirstThrowableOfType(AuthenticationException.class, causeChain);
if (ase == null) {
ase = (AccessDeniedException) throwableAnalyzer.getFirstThrowableOfType(AccessDeniedException.class, causeChain);
}
if (ase != null) {
if (ase instanceof AuthenticationException) {
throw (AuthenticationException) ase;
} else if (ase instanceof AccessDeniedException) {
AccessDeniedException ade = (AccessDeniedException) ase;
if (authenticationTrustResolver.isAnonymous(SecurityContextHolder.getContext().getAuthentication())) {
logger.trace("User session expired or not logged in yet and trying to access unauthorized resource");
HttpServletRequest httpReq = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
if (isAjaxRequest(httpReq)) {
logger.trace("Ajax call detected, redirecting");
/**
* Session expiry check is copied from SessionManagementFilter
* accessdenied check is derived from ExceptionTranslationFilter
* No idea what is the order... This is a workaround.
*/
final String redirectUrl = invalidSessionUrl;
sendAjaxRedirect(resp, httpReq, redirectUrl);
} else {
logger.trace("Normal call detected, redirecting");
new SimpleRedirectInvalidSessionStrategy(invalidSessionUrl).onInvalidSessionDetected(httpReq, resp);
}
} else {
throw ade;
}
}
} else {
throw ex;
}
}
}
public String getInvalidSessionUrl() {
return invalidSessionUrl;
}
public void setInvalidSessionUrl(String invalidSessionUrl) {
this.invalidSessionUrl = invalidSessionUrl;
}
private boolean isAjaxRequest(HttpServletRequest httpReq) {
return new AjaxRedirectUtils().isAjaxRequest(httpReq);
}
private void sendAjaxRedirect(HttpServletResponse resp, HttpServletRequest httpReq, final String redirectUrl) throws IOException {
new AjaxRedirectUtils().sendAjaxRedirect(resp, httpReq, redirectUrl);
}
private static final class DefaultThrowableAnalyzer extends ThrowableAnalyzer {
/**
* #see
* org.springframework.security.web.util.ThrowableAnalyzer#initExtractorMap()
*/
protected void initExtractorMap() {
super.initExtractorMap();
registerExtractor(ServletException.class, new ThrowableCauseExtractor() {
public Throwable extractCause(Throwable throwable) {
ThrowableAnalyzer.verifyThrowableHierarchy(throwable, ServletException.class);
return ((ServletException) throwable).getRootCause();
}
});
}
}
}
In the XML configuration for Spring Security, add the filter after EXCEPTION_TRANSLATION_FILTER so that it can detect access denied
<http>
<custom-filter ref="ajaxTimeoutRedirectFilter" after="EXCEPTION_TRANSLATION_FILTER"/>
</http>
Note that if you use the built-in session management, make sure you do not use the invalid-session-url or related else that will take effect instead
<http>
<session-management>
</session-management>
</http>
This filter also caters for Ajax redirection. I left that out as that is not relevant to the question
In summary, what this achieves is to redirect only if access denied. So non- logged-in users will not get redirected unless they access a unauthorized page. Logged-in users whose sessions expire will get redirected when they access an unauthorized page.
I added spring security filter to my MVC project with java config. The project have a /home method which only allow authenticated user to access.
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/home").authenticated()
.and().formLogin()
.and().httpBasic();
}
which is working as expected, when I request "http://localhost:8080/project/home" it kicks my out to "/login". After successful login, I can now view "/home"
then I add OAuth2, pretty much same setting as Sparklr2 example
#Configuration
public class OAuthServerConfig {
private static final String RESOURCE_ID = "cpe";
#Configuration
#EnableResourceServer
protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.resourceId(RESOURCE_ID).stateless(false);
}
#Override
public void configure(HttpSecurity http) throws Exception {
// #formatter:off
http
// Since we want the protected resources to be accessible in the UI as well we need
// session creation to be allowed (it's disabled by default in 2.0.6)
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.and()
.requestMatchers().antMatchers("/device/**", "/oauth/users/**", "/oauth/clients/**","/me")
.and()
.authorizeRequests()
.antMatchers("/me").access("#oauth2.hasScope('read')")
.antMatchers("/device").access("#oauth2.hasScope('read') or (!#oauth2.isOAuth() and hasRole('ROLE_USER'))")
//.antMatchers("/device/trusted/**").access("#oauth2.hasScope('trust')")
.antMatchers("/device/user/**").access("#oauth2.hasScope('trust')")
.antMatchers("/device/**").access("#oauth2.hasScope('read') or (!#oauth2.isOAuth() and hasRole('ROLE_USER'))")
.antMatchers("/device/register").access("#oauth2.hasScope('write') or (!#oauth2.isOAuth() and hasRole('ROLE_USER'))")
.regexMatchers(HttpMethod.DELETE, "/oauth/users/([^/].*?)/tokens/.*")
.access("#oauth2.clientHasRole('ROLE_CLIENT') and (hasRole('ROLE_USER') or #oauth2.isClient()) and #oauth2.hasScope('write')")
.regexMatchers(HttpMethod.GET, "/oauth/clients/([^/].*?)/users/.*")
.access("#oauth2.clientHasRole('ROLE_CLIENT') and (hasRole('ROLE_USER') or #oauth2.isClient()) and #oauth2.hasScope('read')")
.regexMatchers(HttpMethod.GET, "/oauth/clients/.*")
.access("#oauth2.clientHasRole('ROLE_CLIENT') and #oauth2.isClient() and #oauth2.hasScope('read')");
// #formatter:on
}
}
#Configuration
#EnableAuthorizationServer
protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
#Autowired
private DataSource dataSource;
#Autowired
private TokenStore tokenStore;
#Autowired
private UserApprovalHandler userApprovalHandler;
#Autowired
#Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
//needs to be change
#Value("${tonr.redirect:http://localhost:8080/tonr2/sparklr/redirect}")
private String tonrRedirectUri;
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
//JdbcClientDetailsServiceBuilder
clients.jdbc(dataSource);
}
#Bean
public TokenStore tokenStore() {
//return new InMemoryTokenStore();
return new JdbcTokenStore(dataSource);
}
#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("dragonfly/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 DragonflyUserApprovalHandler userApprovalHandler() throws Exception {
DragonflyUserApprovalHandler handler = new DragonflyUserApprovalHandler();
handler.setApprovalStore(approvalStore());
handler.setRequestFactory(new DefaultOAuth2RequestFactory(clientDetailsService));
handler.setClientDetailsService(clientDetailsService);
handler.setUseApprovalStore(true);
return handler;
}
}
}
with only 1 client detail
client.dataSource(dataSource)
.withClient("my-trusted-client-with-secret")
.authorizedGrantTypes("password", "authorization_code", "refresh_token", "implicit")
.authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
.scopes("read", "write", "trust")
.secret("somesecret");
I run this on my tomcat server, the OAuth works, I make request to /oauth/token, it successfully returns token to me.
I restart my application, then request /home without login, it shows up my home view with full content, without login, I couldn't understand. here is the server log when I request /home
it try to match OAuth filter first, which has Order 0. no match found. then check session, no session found, create a new one.
then it says it is not OAuth request and no token found.
and it continues down the filter chain, AnonymousAuthenticationFilter, then granted ROLE_ANONYMOUS, by that it response to the request with successful.
which is the opposite to my rule .antMatchers("/home").authenticated()
How does that happen?
14:40:51.843 [http-nio-8080-exec-6] DEBUG
o.s.s.w.u.matcher.OrRequestMatcher - Trying to match using Ant
[pattern='/oauth/token'] 14:40:51.843 [http-nio-8080-exec-6] DEBUG
o.s.s.w.u.m.AntPathRequestMatcher - Checking match of request :
'/home'; against '/oauth/token' 14:40:51.843 [http-nio-8080-exec-6]
DEBUG o.s.s.w.u.matcher.OrRequestMatcher - Trying to match using Ant
[pattern='/oauth/token_key'] 14:40:51.843 [http-nio-8080-exec-6] DEBUG
o.s.s.w.u.m.AntPathRequestMatcher - Checking match of request :
'/home'; against '/oauth/token_key' 14:40:51.843
[http-nio-8080-exec-6] DEBUG o.s.s.w.u.matcher.OrRequestMatcher -
Trying to match using Ant [pattern='/oauth/check_token'] 14:40:51.843
[http-nio-8080-exec-6] DEBUG o.s.s.w.u.m.AntPathRequestMatcher -
Checking match of request : '/home'; against '/oauth/check_token'
14:40:51.843 [http-nio-8080-exec-6] DEBUG
o.s.s.w.u.matcher.OrRequestMatcher - No matches found 14:40:51.843
[http-nio-8080-exec-6] DEBUG o.s.s.w.u.matcher.OrRequestMatcher -
Trying to match using
org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfiguration$NotOAuthRequestMatcher#7926d3d3
14:40:51.843 [http-nio-8080-exec-6] DEBUG
o.s.s.w.u.matcher.OrRequestMatcher - matched 14:40:51.843
[http-nio-8080-exec-6] DEBUG o.s.security.web.FilterChainProxy - /home
at position 1 of 11 in additional filter chain; firing Filter:
'WebAsyncManagerIntegrationFilter' 14:40:51.844 [http-nio-8080-exec-6]
DEBUG o.s.security.web.FilterChainProxy - /home at position 2 of 11 in
additional filter chain; firing Filter:
'SecurityContextPersistenceFilter' 14:40:51.844 [http-nio-8080-exec-6]
DEBUG o.s.s.w.c.HttpSessionSecurityContextRepository - No HttpSession
currently exists 14:40:51.844 [http-nio-8080-exec-6] DEBUG
o.s.s.w.c.HttpSessionSecurityContextRepository - No SecurityContext
was available from the HttpSession: null. A new one will be created.
14:40:51.844 [http-nio-8080-exec-6] DEBUG
o.s.security.web.FilterChainProxy - /home at position 3 of 11 in
additional filter chain; firing Filter: 'HeaderWriterFilter'
14:40:51.844 [http-nio-8080-exec-6] DEBUG
o.s.s.w.h.writers.HstsHeaderWriter - Not injecting HSTS header since
it did not match the requestMatcher
org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher#3d823ea7
14:40:51.844 [http-nio-8080-exec-6] DEBUG
o.s.security.web.FilterChainProxy - /home at position 4 of 11 in
additional filter chain; firing Filter: 'LogoutFilter' 14:40:51.844
[http-nio-8080-exec-6] DEBUG o.s.s.w.u.m.AntPathRequestMatcher -
Checking match of request : '/home'; against '/logout' 14:40:51.844
[http-nio-8080-exec-6] DEBUG o.s.security.web.FilterChainProxy - /home
at position 5 of 11 in additional filter chain; firing Filter:
'OAuth2AuthenticationProcessingFilter' 14:40:51.844
[http-nio-8080-exec-6] DEBUG o.s.s.o.p.a.BearerTokenExtractor - Token
not found in headers. Trying request parameters. 14:40:51.844
[http-nio-8080-exec-6] DEBUG o.s.s.o.p.a.BearerTokenExtractor - Token
not found in request parameters. Not an OAuth2 request. 14:40:51.844
[http-nio-8080-exec-6] DEBUG
o.s.s.o.p.a.OAuth2AuthenticationProcessingFilter - No token in
request, will continue chain. 14:40:51.844 [http-nio-8080-exec-6]
DEBUG o.s.security.web.FilterChainProxy - /home at position 6 of 11 in
additional filter chain; firing Filter: 'RequestCacheAwareFilter'
14:40:51.844 [http-nio-8080-exec-6] DEBUG
o.s.security.web.FilterChainProxy - /home at position 7 of 11 in
additional filter chain; firing Filter:
'SecurityContextHolderAwareRequestFilter' 14:40:51.844
[http-nio-8080-exec-6] DEBUG o.s.security.web.FilterChainProxy - /home
at position 8 of 11 in additional filter chain; firing Filter:
'AnonymousAuthenticationFilter' 14:40:51.844 [http-nio-8080-exec-6]
DEBUG o.s.s.w.a.AnonymousAuthenticationFilter - Populated
SecurityContextHolder with anonymous token:
'org.springframework.security.authentication.AnonymousAuthenticationToken#9055c2bc:
Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated:
true; Details:
org.springframework.security.web.authentication.WebAuthenticationDetails#b364:
RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null; Granted
Authorities: ROLE_ANONYMOUS' 14:40:51.844 [http-nio-8080-exec-6] DEBUG
o.s.security.web.FilterChainProxy - /home at position 9 of 11 in
additional filter chain; firing Filter: 'SessionManagementFilter'
14:40:51.844 [http-nio-8080-exec-6] DEBUG
o.s.security.web.FilterChainProxy - /home at position 10 of 11 in
additional filter chain; firing Filter: 'ExceptionTranslationFilter'
14:40:51.844 [http-nio-8080-exec-6] DEBUG
o.s.security.web.FilterChainProxy - /home at position 11 of 11 in
additional filter chain; firing Filter: 'FilterSecurityInterceptor'
14:40:51.844 [http-nio-8080-exec-6] DEBUG
o.s.s.w.u.m.AntPathRequestMatcher - Checking match of request :
'/home'; against '/me' 14:40:51.844 [http-nio-8080-exec-6] DEBUG
o.s.s.w.u.m.AntPathRequestMatcher - Checking match of request :
'/home'; against '/device' 14:40:51.844 [http-nio-8080-exec-6] DEBUG
o.s.s.w.u.m.AntPathRequestMatcher - Checking match of request :
'/home'; against '/device/user/' 14:40:51.844 [http-nio-8080-exec-6]
DEBUG o.s.s.w.u.m.AntPathRequestMatcher - Checking match of request :
'/home'; against '/device/' 14:40:51.844 [http-nio-8080-exec-6]
DEBUG o.s.s.w.u.m.AntPathRequestMatcher - Checking match of request :
'/home'; against '/device/register' 14:40:51.844
[http-nio-8080-exec-6] DEBUG o.s.s.w.u.m.RegexRequestMatcher -
Checking match of request : '/home'; against
'/oauth/clients/([^/].?)/users/.' 14:40:51.844
[http-nio-8080-exec-6] DEBUG o.s.s.w.u.m.RegexRequestMatcher -
Checking match of request : '/home'; against '/oauth/clients/.'
14:40:51.844 [http-nio-8080-exec-6] DEBUG
o.s.s.w.a.i.FilterSecurityInterceptor - Public object - authentication
not attempted 14:40:51.844 [http-nio-8080-exec-6] DEBUG
o.s.security.web.FilterChainProxy - /home reached end of additional
filter chain; proceeding with original chain 14:40:51.844
[http-nio-8080-exec-6] DEBUG o.s.web.servlet.DispatcherServlet -
DispatcherServlet with name 'dispatcher' processing GET request for
[/Dragonfly/home] 14:40:51.844 [http-nio-8080-exec-6] DEBUG
o.s.w.s.m.m.a.RequestMappingHandlerMapping - Looking up handler method
for path /home 14:40:51.845 [http-nio-8080-exec-6] DEBUG
o.s.w.s.m.m.a.RequestMappingHandlerMapping - Returning handler method
[public java.lang.String
com.umedia.Dragonfly.controller.HomeController.home()] 14:40:51.845
[http-nio-8080-exec-6] DEBUG o.s.b.f.s.DefaultListableBeanFactory -
Returning cached instance of singleton bean 'homeController'
14:40:51.845 [http-nio-8080-exec-6] DEBUG
o.s.web.servlet.DispatcherServlet - Last-Modified value for
[/Dragonfly/home] is: -1 14:40:51.845 [http-nio-8080-exec-6] DEBUG
o.s.web.servlet.DispatcherServlet - Rendering view
[org.springframework.web.servlet.view.JstlView: name 'home'; URL
[/WEB-INF/views/home.jsp]] in DispatcherServlet with name 'dispatcher'
14:40:51.845 [http-nio-8080-exec-6] DEBUG
o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of
singleton bean 'requestDataValueProcessor' 14:40:51.845
[http-nio-8080-exec-6] DEBUG o.s.web.servlet.view.JstlView -
Forwarding to resource [/WEB-INF/views/home.jsp] in
InternalResourceView 'home' 14:40:51.847 [http-nio-8080-exec-6] DEBUG
o.s.web.servlet.DispatcherServlet - Successfully completed request
14:40:51.847 [http-nio-8080-exec-6] DEBUG
o.s.s.w.a.ExceptionTranslationFilter - Chain processed normally
14:40:51.847 [http-nio-8080-exec-6] DEBUG
o.s.s.w.c.HttpSessionSecurityContextRepository - SecurityContext is
empty or contents are anonymous - context will not be stored in
HttpSession. 14:40:51.847 [http-nio-8080-exec-6] DEBUG
o.s.s.w.c.SecurityContextPersistenceFilter - SecurityContextHolder now
cleared, as request processing completed 14:40:51.865
[http-nio-8080-exec-7] DEBUG o.s.s.w.u.matcher.OrRequestMatcher -
Trying to match using Ant [pattern='/oauth/token'] 14:40:51.865
[http-nio-8080-exec-7] DEBUG o.s.s.w.u.m.AntPathRequestMatcher -
Checking match of request : '/resources/05.jpg'; against
'/oauth/token' 14:40:51.865 [http-nio-8080-exec-7] DEBUG
o.s.s.w.u.matcher.OrRequestMatcher - Trying to match using Ant
[pattern='/oauth/token_key'] 14:40:51.865 [http-nio-8080-exec-7] DEBUG
o.s.s.w.u.m.AntPathRequestMatcher - Checking match of request :
'/resources/05.jpg'; against '/oauth/token_key' 14:40:51.865
[http-nio-8080-exec-7] DEBUG o.s.s.w.u.matcher.OrRequestMatcher -
Trying to match using Ant [pattern='/oauth/check_token'] 14:40:51.865
[http-nio-8080-exec-7] DEBUG o.s.s.w.u.m.AntPathRequestMatcher -
Checking match of request : '/resources/05.jpg'; against
'/oauth/check_token' 14:40:51.865 [http-nio-8080-exec-7] DEBUG
o.s.s.w.u.matcher.OrRequestMatcher - No matches found 14:40:51.865
[http-nio-8080-exec-7] DEBUG o.s.s.w.u.matcher.OrRequestMatcher -
Trying to match using
org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfiguration$NotOAuthRequestMatcher#7926d3d3
14:40:51.865 [http-nio-8080-exec-7] DEBUG
o.s.s.w.u.matcher.OrRequestMatcher - matched 14:40:51.865
[http-nio-8080-exec-7] DEBUG o.s.security.web.FilterChainProxy -
/resources/05.jpg at position 1 of 11 in additional filter chain;
firing Filter: 'WebAsyncManagerIntegrationFilter' 14:40:51.865
[http-nio-8080-exec-7] DEBUG o.s.security.web.FilterChainProxy -
/resources/05.jpg at position 2 of 11 in additional filter chain;
firing Filter: 'SecurityContextPersistenceFilter' 14:40:51.865
[http-nio-8080-exec-7] DEBUG
o.s.s.w.c.HttpSessionSecurityContextRepository - HttpSession returned
null object for SPRING_SECURITY_CONTEXT 14:40:51.865
[http-nio-8080-exec-7] DEBUG
o.s.s.w.c.HttpSessionSecurityContextRepository - No SecurityContext
was available from the HttpSession:
org.apache.catalina.session.StandardSessionFacade#ba8ab6a. A new one
will be created. 14:40:51.865 [http-nio-8080-exec-7] DEBUG
o.s.security.web.FilterChainProxy - /resources/05.jpg at position 3 of
11 in additional filter chain; firing Filter: 'HeaderWriterFilter'
14:40:51.865 [http-nio-8080-exec-7] DEBUG
o.s.s.w.h.writers.HstsHeaderWriter - Not injecting HSTS header since
it did not match the requestMatcher
org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher#3d823ea7
14:40:51.865 [http-nio-8080-exec-7] DEBUG
o.s.security.web.FilterChainProxy - /resources/05.jpg at position 4 of
11 in additional filter chain; firing Filter: 'LogoutFilter'
14:40:51.865 [http-nio-8080-exec-7] DEBUG
o.s.s.w.u.m.AntPathRequestMatcher - Checking match of request :
'/resources/05.jpg'; against '/logout' 14:40:51.865
[http-nio-8080-exec-7] DEBUG o.s.security.web.FilterChainProxy -
/resources/05.jpg at position 5 of 11 in additional filter chain;
firing Filter: 'OAuth2AuthenticationProcessingFilter' 14:40:51.865
[http-nio-8080-exec-7] DEBUG o.s.s.o.p.a.BearerTokenExtractor - Token
not found in headers. Trying request parameters. 14:40:51.865
[http-nio-8080-exec-7] DEBUG o.s.s.o.p.a.BearerTokenExtractor - Token
not found in request parameters. Not an OAuth2 request. 14:40:51.865
[http-nio-8080-exec-7] DEBUG
o.s.s.o.p.a.OAuth2AuthenticationProcessingFilter - No token in
request, will continue chain. 14:40:51.865 [http-nio-8080-exec-7]
DEBUG o.s.security.web.FilterChainProxy - /resources/05.jpg at
position 6 of 11 in additional filter chain; firing Filter:
'RequestCacheAwareFilter' 14:40:51.865 [http-nio-8080-exec-7] DEBUG
o.s.security.web.FilterChainProxy - /resources/05.jpg at position 7 of
11 in additional filter chain; firing Filter:
'SecurityContextHolderAwareRequestFilter' 14:40:51.865
[http-nio-8080-exec-7] DEBUG o.s.security.web.FilterChainProxy -
/resources/05.jpg at position 8 of 11 in additional filter chain;
firing Filter: 'AnonymousAuthenticationFilter' 14:40:51.865
[http-nio-8080-exec-7] DEBUG o.s.s.w.a.AnonymousAuthenticationFilter -
Populated SecurityContextHolder with anonymous token:
'org.springframework.security.authentication.AnonymousAuthenticationToken#6faeba70:
Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated:
true; Details:
org.springframework.security.web.authentication.WebAuthenticationDetails#fffbcba8:
RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId:
737F9CEEE6747FABCB433614EF76CF3B; Granted Authorities: ROLE_ANONYMOUS'
14:40:51.865 [http-nio-8080-exec-7] DEBUG
o.s.security.web.FilterChainProxy - /resources/05.jpg at position 9 of
11 in additional filter chain; firing Filter:
'SessionManagementFilter' 14:40:51.865 [http-nio-8080-exec-7] DEBUG
o.s.security.web.FilterChainProxy - /resources/05.jpg at position 10
of 11 in additional filter chain; firing Filter:
'ExceptionTranslationFilter' 14:40:51.865 [http-nio-8080-exec-7] DEBUG
o.s.security.web.FilterChainProxy - /resources/05.jpg at position 11
of 11 in additional filter chain; firing Filter:
'FilterSecurityInterceptor' 14:40:51.865 [http-nio-8080-exec-7] DEBUG
o.s.s.w.u.m.AntPathRequestMatcher - Checking match of request :
'/resources/05.jpg'; against '/me' 14:40:51.865 [http-nio-8080-exec-7]
DEBUG o.s.s.w.u.m.AntPathRequestMatcher - Checking match of request :
'/resources/05.jpg'; against '/device' 14:40:51.865
[http-nio-8080-exec-7] DEBUG o.s.s.w.u.m.AntPathRequestMatcher -
Checking match of request : '/resources/05.jpg'; against
'/device/user/' 14:40:51.865 [http-nio-8080-exec-7] DEBUG
o.s.s.w.u.m.AntPathRequestMatcher - Checking match of request :
'/resources/05.jpg'; against '/device/' 14:40:51.865
[http-nio-8080-exec-7] DEBUG o.s.s.w.u.m.AntPathRequestMatcher -
Checking match of request : '/resources/05.jpg'; against
'/device/register' 14:40:51.865 [http-nio-8080-exec-7] DEBUG
o.s.s.w.u.m.RegexRequestMatcher - Checking match of request :
'/resources/05.jpg'; against '/oauth/clients/([^/].?)/users/.'
14:40:51.865 [http-nio-8080-exec-7] DEBUG
o.s.s.w.u.m.RegexRequestMatcher - Checking match of request :
'/resources/05.jpg'; against '/oauth/clients/.' 14:40:51.865
[http-nio-8080-exec-7] DEBUG o.s.s.w.a.i.FilterSecurityInterceptor -
Public object - authentication not attempted 14:40:51.865
[http-nio-8080-exec-7] DEBUG o.s.security.web.FilterChainProxy -
/resources/05.jpg reached end of additional filter chain; proceeding
with original chain 14:40:51.865 [http-nio-8080-exec-7] DEBUG
o.s.web.servlet.DispatcherServlet - DispatcherServlet with name
'dispatcher' processing GET request for [/Dragonfly/resources/05.jpg]
14:40:51.865 [http-nio-8080-exec-7] DEBUG
o.s.w.s.m.m.a.RequestMappingHandlerMapping - Looking up handler method
for path /resources/05.jpg 14:40:51.866 [http-nio-8080-exec-7] DEBUG
o.s.w.s.m.m.a.RequestMappingHandlerMapping - Did not find handler
method for [/resources/05.jpg] 14:40:51.866 [http-nio-8080-exec-7]
DEBUG o.s.s.o.p.e.FrameworkEndpointHandlerMapping - Looking up handler
method for path /resources/05.jpg 14:40:51.866 [http-nio-8080-exec-7]
DEBUG o.s.s.o.p.e.FrameworkEndpointHandlerMapping - Did not find
handler method for [/resources/05.jpg] 14:40:51.866
[http-nio-8080-exec-7] DEBUG o.s.w.s.h.SimpleUrlHandlerMapping -
Matching patterns for request [/resources/05.jpg] are [/resources/**]
14:40:51.866 [http-nio-8080-exec-7] DEBUG
o.s.w.s.h.SimpleUrlHandlerMapping - URI Template variables for request
[/resources/05.jpg] are {} 14:40:51.866 [http-nio-8080-exec-7] DEBUG
o.s.w.s.h.SimpleUrlHandlerMapping - Mapping [/resources/05.jpg] to
HandlerExecutionChain with handler [ResourceHttpRequestHandler
[locations=[ServletContext resource [/resources/]],
resolvers=[org.springframework.web.servlet.resource.PathResourceResolver#20458412]]]
and 1 interceptor 14:40:51.866 [http-nio-8080-exec-7] DEBUG
o.s.web.servlet.DispatcherServlet - Last-Modified value for
[/Dragonfly/resources/05.jpg] is: -1 14:40:51.867
[http-nio-8080-exec-7] DEBUG o.s.web.servlet.DispatcherServlet - Null
ModelAndView returned to DispatcherServlet with name 'dispatcher':
assuming HandlerAdapter completed request handling 14:40:51.867
[http-nio-8080-exec-7] DEBUG o.s.web.servlet.DispatcherServlet -
Successfully completed request 14:40:51.867 [http-nio-8080-exec-7]
DEBUG o.s.s.w.a.ExceptionTranslationFilter - Chain processed normally
14:40:51.867 [http-nio-8080-exec-7] DEBUG
o.s.s.w.c.HttpSessionSecurityContextRepository - SecurityContext is
empty or contents are anonymous - context will not be stored in
HttpSession. 14:40:51.867 [http-nio-8080-exec-7] DEBUG
o.s.s.w.c.SecurityContextPersistenceFilter - SecurityContextHolder now
cleared, as request processing completed
it looks like you have problems with your project setup and pom configuration
You added spring boot dependencies, yet you don't use spring boot.
Your project is package as jar, yet you you have WEB-INF and use WebApplicationInitializer rather than spring boot
Your pom dependencies is wrong
I have modified several things:
move WebContent folder and rename it to src/main/webapp
update your pom configuration
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.umedia</groupId>
<artifactId>Dragonfly</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>Dragonfly</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.2.0.RELEASE</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.4.5</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.4.5</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jsp-api</artifactId>
<version>2.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>9.4-1201-jdbc41</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.2.0.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.0.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.mariadb.jdbc</groupId>
<artifactId>mariadb-java-client</artifactId>
<version>1.1.7</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.0</version>
<configuration>
<path>/</path>
</configuration>
</plugin>
</plugins>
<finalName>liveBLE</finalName>
</build>
</project>
run it using mvn tomcat7:run. If I access /home I will be redirected to login page and if I access /device i will get
<oauth>
<error_description>
Full authentication is required to access this resource
</error_description>
<error>unauthorized</error>
</oauth>
This is the expected behavior using OAuth and Spring security.
I'm using Spring Security with Thymeleaf and want to create a login and a register form on different sites that make both use of CSRF protection. Protecting the login site is easy, as with the folloing WebSecurity configuration
#Override
protected void configure(final HttpSecurity http) throws Exception {
http
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.requestMatchers()
.antMatchers("/login", "/oauth/authorize", "/oauth/confirm_access")
.and()
.authorizeRequests()
.anyRequest()
.authenticated();
}
Spring supports adding CSRF protection in general by the Security Filter Chain that is build in the configure method. This Filter Chain contains a CSRFFilter that adds/evaluates the CSRF token. This Filter Chain is then used for all matches defined in the above configuration. The mechanism of getting the Filters that are applied to a request can be found here in the method
doFilterInternal(ServletRequest, ServletResponse, FilterChain)
The problem is, if I add the "/register" site to this configuration, the user is redirected to the "/login" site first. If I don't add it to the above config, the mentioned FilterChain is not applied (and so not the CsrfFilter).
So what I want is to reuse the CsrfFilter in the Filter Chain of the "/register" site, but I don't know how to do that.
I'd prefer this approach to other ideas like writing a custom CSRF filter as suggested here or here.
From all of this i understood the problem is that you want people to access /register without needing to login first. Which is a simple fix:
#Override
protected void configure(final HttpSecurity http) throws Exception {
http
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.requestMatchers()
// add this line
.antMatchers("/register").permitAll().and
//
.antMatchers("/login", "/oauth/authorize", "/oauth/confirm_access")
.and()
.authorizeRequests()
.anyRequest()
.authenticated();
}
Turned out that the Spring Security Filter chain is applied to all endpoints mentioned in the list provided to requestMatchers().antMatchers().
So to use CSRF protection for a site that is not the login site, I just had to add it to this list and then permit all access to it, so there is no redirect to the login page. My final config looks like this
#Override
protected void configure(final HttpSecurity http) throws Exception {
http.requestMatchers()
// consider these patterns for this config and the Security Filter Chain
.antMatchers("/login", "/register", "/oauth/authorize", "/oauth/confirm_access", "/oauth/token_key",
"/oauth/check_token", "/oauth/error")
.and()
// define authorization behaviour
.authorizeRequests()
// /register is allowed by anyone
.antMatchers("/register").permitAll()
// /oauth/authorize needs authentication; enables redirect to /login
.antMatchers("/oauth/authorize").authenticated()
// any other request in the patterns above are forbidden
.anyRequest().denyAll()
.and()
.formLogin()
// we have a custom login page at /login
// that is permitted to everyone
.loginPage("/login").permitAll();
}