I've searched for a solution but can't find one anywhere, at least not a current one or one that uses non-xml based Spring and Spring Security configuration.
I need to implement a handler that will be used prior to the spring logout handler. I've read plenty of articles about the LogoutSuccessHandler but that is called after a successful logout by the Logout Filter and I need to access user data that is stored in the users session to perform some database entries, site logout info, etc. This session is lost once spring logs out the user so it has to be before that.
I've tried creating my own custom logout class and defined it in my application configuration class like this:
#Bean
public CustomLogoutHandler customLogoutHandler() {
return new CustomLogoutHandler();
}
and my class extends the LogoutHandler like the spring documents say to do:
public class CustomLogoutHandler extends LogoutHandler {
public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
// business logic here
}
}
This is still not working. I put a breakpoint in the code and it never gets picked up. Does anyone have an idea of what could be causing this or what I need to do to get it to work?
To use your own custom logout handler that implements Spring's LogoutHandler.class you need to let Spring know that you are using your own in the configuration file under the logout options using .addLogoutHandler. I think you were missing this step. In the security config file:
public class SecurityConfig extends WebSecurityConfigurerAdapter {
... // Other methods here
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.otherConfigOptions
.logout()
.addLogoutHandler(customLogoutHandler()) <- custom handler
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.otherConfigOptions....
}
}
And define the bean, I put mine in the SecurityConfig.class but I think you can put it in the web or app config class depending on how you set up your project.
#Bean
public CustomLogoutHandler customLogoutHandler() {
return new CustomLogoutHandler();
}
Then, create your CustomLogoutHandler.class, making sure to IMPLEMENT the LogoutHandler and OVERRIDE the logout method. Here you can use the Authentication class to access anything you have added to the users request scope.
public class CustomLogoutHandler implements LogoutHandler {
#Override
public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
// business logic here
}
}
You should also take a look at this question and answer which talks about the order of custom handler mappings in Spring.
I hope this helps.
Related
I want to redirect users to a different page after login depending on what type of user they are, and if they're on a mobile device. My project uses Spring MVC and Spring Security. To get the redirect logic working, I use a Spring Security AuthenticationSuccessHandler to identify the type of user post-login and direct them to the correct page.
I was hoping to use Spring Mobile to check at this point whether they're on a mobile device, and if so send them to the mobile version of the URL. However, it seems that interceptors such as DeviceResolverHandlerInterceptor get executed after the Spring Security filters, and so the resolution hasn't happened at the point I need it to.
At the moment, the only solution I can see to this is to disable the interceptor and instead directly use LiteDeviceResolver's resolveDevice() method. This feels a little bit hacky, but I can't see another obvious choice.
Is there any better way to configure this people are aware of?
#Configuration
public class WebConfig implements WebMvcConfigurer {
#Bean
public DeviceResolverHandlerInterceptor deviceResolverHandlerInterceptor() {
return new DeviceResolverHandlerInterceptor();
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(deviceResolverHandlerInterceptor()).order(Ordered.HIGHEST_PRECEDENCE);
}
}
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
boolean isMobile = DeviceUtils.getCurrentDevice(request) != null
&& DeviceUtils.getCurrentDevice(request).isMobile();
...
I developed a Microservice Architecture with Spring. Therefor I have implemented one auth service and one userinterface service.
The Session is shared with redis. So The worklow is that you login on the auth-login page and after success you get redirected to the ui resource.
So far so good. I also implemented authorization by user role the secure my resources on the controller level.
What I want:
Even when login was successful it should not be possible change the url and enter a restricted area. So when a client tries to do this an Access-denied Exception gets thrown atm.
In that case I want to invalidate the session and redirect to the login page of the auth service.
What I did:
I followed this tutorial by baeldung to implement a custom accessdeniedhandler.
Code:
CustomAccessDeniedHandler.java
public class CustomAccessDeniedHandler implements AccessDeniedHandler {
#Override
public void handle(HttpServletRequest request, HttpServletResponse response,
AccessDeniedException accessDeniedException) throws IOException, ServletException {
response.sendRedirect("http://localhost:8000/login");
}
}
SpringSecurity.java
#Configuration
public class SpringSecurityConfiguration extends WebSecurityConfigurerAdapter{
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated();
http.exceptionHandling().accessDeniedHandler(accessDeniedHandler());
}
#Bean
public AccessDeniedHandler accessDeniedHandler(){
return new CustomAccessDeniedHandler();
}
}
What happens:
I've set a breakpoint in my handler but it is not thrown and the general access-denied message gets thrown. Is there something I forgot to register in the implementation?
Thank you guys
TL;DR
Is it possible to control the session creation policy in Spring (Security) on a per request basis?
Long version...
I have been using normal login form user authentication for our application.
Some of the controllers are #RestControllers and up to now, the default user session tracked by cookie has allowed it to work fine.
(I.e. when an XHR request comes from a page, the request is authenticated to the previously logged in user as the browser sends the JSESSIONID cookie as usual)
I now want to allow some of the #RestController end points to be called from a rest client, rather than browser, so I have created an API token authentication scheme - this works fine.
One of the last bits of cleanup is that the REST calls generate a session, which I'd like to avoid if possible.
I can't set the session policy to NEVER (because i'm still relying on sessions for my web users).
I have tried IF_REQUIRED to no avail.
I have looked at the HttpSessionSecurityContextRepository but it wraps the request, and a session is created whenever the response is flushed.
(See stacktrace below)
Is it possible elsewhere to hook into the session management on a per-request basis?
I can distinguish the type of request easily enough based on the class type of the Authentication object.
at myapp.cfg.WebConfig$1.sessionCreated(WebConfig.java:74)
at io.undertow.servlet.core.ApplicationListeners.sessionCreated(ApplicationListeners.java:300)
at io.undertow.servlet.core.SessionListenerBridge.sessionCreated(SessionListenerBridge.java:56)
at io.undertow.server.session.SessionListeners.sessionCreated(SessionListeners.java:52)
at io.undertow.server.session.InMemorySessionManager.createSession(InMemorySessionManager.java:187)
at io.undertow.servlet.spec.ServletContextImpl.getSession(ServletContextImpl.java:741)
at io.undertow.servlet.spec.HttpServletRequestImpl.getSession(HttpServletRequestImpl.java:370)
at javax.servlet.http.HttpServletRequestWrapper.getSession(HttpServletRequestWrapper.java:270)
at org.springframework.security.web.context.HttpSessionSecurityContextRepository$SaveToSessionResponseWrapper.createNewSessionIfAllowed(HttpSessionSecurityContextRepository.java:427)
at org.springframework.security.web.context.HttpSessionSecurityContextRepository$SaveToSessionResponseWrapper.saveContext(HttpSessionSecurityContextRepository.java:364)
at org.springframework.security.web.context.SaveContextOnUpdateOrErrorResponseWrapper.onResponseCommitted(SaveContextOnUpdateOrErrorResponseWrapper.java:85)
at org.springframework.security.web.util.OnCommittedResponseWrapper.doOnResponseCommitted(OnCommittedResponseWrapper.java:245)
at org.springframework.security.web.util.OnCommittedResponseWrapper.access$000(OnCommittedResponseWrapper.java:33)
at org.springframework.security.web.util.OnCommittedResponseWrapper$SaveContextServletOutputStream.flush(OnCommittedResponseWrapper.java:512)
at org.springframework.security.web.util.OnCommittedResponseWrapper$SaveContextServletOutputStream.flush(OnCommittedResponseWrapper.java:513)
at com.fasterxml.jackson.core.json.UTF8JsonGenerator.flush(UTF8JsonGenerator.java:1050)
at com.fasterxml.jackson.databind.ObjectWriter.writeValue(ObjectWriter.java:953)
Split your security configuration into separate sections for a form login (session based API access) and a stateless API token authentication scheme.
Example:
#Configuration
#EnableWebSecurity
public class SecurityConfiguration {
#Order(1)
#Configuration
class ApiSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/api/**")
.authorizeRequests().anyRequest().authenticated()
.and()
.httpBasic().realmName("API") // your API token authentication scheme
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER)
.and()
.exceptionHandling().authenticationEntryPoint(new Http401AuthenticationEntryPoint("Form realm=\"API\"")); // prevent basic authentication popup in browser
}
}
#Order(2)
#Configuration
class DefaultSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.formLogin().loginPage("/login").permitAll()
.and()
.logout().logoutSuccessUrl("/login").permitAll();
}
}
}
Replace .httpBasic().realmName("API") with you own authentication scheme.
Call your API with e.g. curl -v ... and verify that there is no Set-Cookie header in the response. Otherwise your code somewhere creates an http session on its own.
You should try create-session policy as "stateless" for your API end points.
If "stateless" is used, this implies that the
application guarantees that it will not create a session. This differs from the use of
"never" which mans that Spring Security will not create a session, but will make use of
one if the application does.
I had the exact same problem and could not find a clean solution. In the absence of better options, I'll post a semi working hack.
DISCLAIMER: I have not used this solution (I fell back to sessions, at least for now), try it at your own risk.
Override the default SecurityContextRepository:
#Component
public class CustomSecurityContextRepository extends HttpSessionSecurityContextRepository {
#Override
public SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder) {
SecurityContext securityContext = super.loadContext(requestResponseHolder);
// disable automatic saving of security context on response committed
// WARNING: not sure how safe this is
SaveContextOnUpdateOrErrorResponseWrapper response =
(SaveContextOnUpdateOrErrorResponseWrapper)requestResponseHolder.getResponse();
response.disableSaveOnResponseCommitted();
return securityContext;
}
#Override
public void saveContext(SecurityContext context, HttpServletRequest request,
HttpServletResponse response) {
Authentication authentication = context.getAuthentication();
// call super.saveContext according to your use case
}
}
Finally, register this class in the WebSecurityConfigurerAdapter:
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.securityContext().securityContextRepository(customSecurityContextRepository);
}
If anyone has a better solution I would be interested in hearing it.
I have a simple Spring Boot application which exposes a REST API.
I have successfully configured the spring security to secure every method in the rest API according to its ROLE, using the #PreAuthorize("hasRole('ROLE_4')") annotation.
I have noticed that If I don't put the #PreAuthorize annotation at all, the framework allows this request to any authenticated user. I want to reverse this behavior. So if one of the programmers will forget to add the #PreAuthorize annotation, any request to this method will be rejected automatically.
Here is my configuration:
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
//Disable HTTP Basic authentication
http.httpBasic().disable();
//Add the filters
http.addFilterBefore(new AuthenticationFilter(authenticationManager()), BasicAuthenticationFilter.class);
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(securityServiceAuthenticationProvider());
}
#Bean
public AuthenticationProvider securityServiceAuthenticationProvider() {
return new SecurityServiceAuthenticationProvider();
}
}
Thanks
Guy Hudara
You can use a MethodSecurityInterceptor; sample XML configuration is here. The example applies security to a single bean, but the expressions are very flexible, you can protect e.g. all public members of any class with name ending in "Controller". I have used a similar XML configuration before, but I haven't done this with Java configuration; but I suppose you can do the same thing in Java configuration.
You could specify a custom AccessDecisionManager and, if the queried object is a MethodInvocation, then check if it has a #PreAuthorize annotation; if yes, let it pass. Otherwise, fail it. You can add it to the configuration like this: http.authorizeRequests().accessDecisionManager(myManager).
I've configured a custom authentication provider, a success handler and a failure handler in Spring Security (v4.0.1). When using the default ones, after displaying the login page, the user was redirected to the previously requested url. However, I lost that behaviour when implementing my own ones, so I'm trying to recover it.
Basically, right now, I'm being redirected to the home page everytime I log in, even I've accesed the login page trying to fetch another resource (in my case /web/users). That's the configuration I have right now:
#Configuration bean (extends WebSecurityConfigurerAdapter)
#Override
protected void configure(HttpSecurity http) throws Exception {
http.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/web/logout"))
.invalidateHttpSession(true).logoutSuccessUrl("/web/login");
http.authorizeRequests()
.antMatchers("/javax.faces.resource/**")
.permitAll()
.antMatchers("/web/recovery").permitAll()
.antMatchers("/web/users").authenticated().anyRequest()
.authenticated().and().formLogin()
.failureHandler(failureHandler).loginPage("/web/login")
.loginProcessingUrl("/web/j_spring_security_check")
.successHandler(successHandler).permitAll();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth,
DataSource ds, PasswordEncoder pwdEncoder) throws Exception {
auth.authenticationProvider(authProvider);
}
The custom AuthenticationSuccessHandler
public class SystemAuthenticationSuccessHandler extends
SavedRequestAwareAuthenticationSuccessHandler {
private IUserService service;
public SystemAuthenticationSuccessHandler(IUserService service) {
this.service = service;
setDefaultTargetUrl("/web/home");
}
#Override
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response, Authentication authentication)
throws IOException, ServletException {
String username = request.getParameter("username");
User user = service.findByIdOrEmail(username, username);
if (user != null) {
service.saveLoginSuccess(user.getId());
}
//Call the parent method to manage the successful authentication
super.onAuthenticationSuccess(request, response, authentication);
}
Basically, the problem I have is requestCache is always returning null for current request in the SavedRequestAwareAuthenticationSuccessHandler#onAuthenticationSuccess method and that's because the HttpSessionRequestCache#saveRequest method doesn't match my incoming request before being redirected to the login page.
Specifically, the HttpSessionRequestCache Spring is using is an AndRequestMatcher which discards all my incoming requests. I want it to use the AnyRequestMatcher, but don't know how to tell Spring that.
Update
Having set a breakpoint in HttpSessionRequestCache#setRequestMatcher, that's the concrete point where Spring Security sets it:
However, I don't know how to set a custom request cache configurer for my case! Isn't there an easier way for doing it?
Update 2
I've discovered right now this issue only happens when using Firefox and not with Chrome or Internet Explorer.
It's actually a problem with my current firefox browser. Even if I remove all the navigation and cache data, it keeps happening, but not with other browsers as Chrome or IE, not even with other FF browsers.