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
Related
I am working on a spring boot app where I am adding a custom filter and adding method addFilterBefore.
This is my config configure method:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.cors().and().authorizeRequests()
.anyRequest().authenticated();
http.addFilterBefore(btoBSecurityFilter,UsernamePasswordAuthenticationFilter.class);
http.httpBasic();
}
Here's my Custom Filter:
#Component
public class BtoBSecurityFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
//I dont want to use this as I dont want to use UsernamePasswordAuthenticationFilter.
SecurityContextHolder.getContext().setAuthentication(getAuthentication(7312L,"tokenlelo"));
filterChain.doFilter(httpServletRequest,httpServletResponse);
logger.info("hey aaya");
}
// dont want to use this.
public Authentication getAuthentication(Long userId, String token) {
UserDetails userDetails = org.springframework.security.core.userdetails.User
.withUsername("")
.password("")
.build();
return new UsernamePasswordAuthenticationToken("", "", userDetails.getAuthorities());
}
}
Now the above code works but I dont want the UsernamePasswordAuthenticationFilter to happen in my case and I just want my customFilter to filter out requests for me.
How can I do this? I am not able to use addFilter method as it gives order errors.
I just want my custom filter to run and dont want SecurityContext to set or save anything.
Can someone help me with this?
Usage of UsernamePasswordAuthenticationToken doesn't guarantee that UsernamePasswordAuthenticationFilter will filter your request.
If you look at source code (using your IDEA or github), especially at AbstractAuthenticationProcessingFilter method boolean requiresAuthentication(HttpServletRequest request, HttpServletResponse response), and at UsernamePasswordAuthenticationFilter constructors, you'll see that UsernamePasswordAuthenticationFilter only filters requests which match the authentication request (default is POST-request to "/login").
As you set your custom filter before UsernamePasswordAuthenticationFilter, this filter will be invoked anyway after your custom filter, unless you skip filterChain.doFilter(httpServletRequest,httpServletResponse) method in your custom filter code.
But, as I said before, it doesn't mean UsernamePasswordAuthenticationFilter will filter your request.
So, it doesn't depend on using UsernamePasswordAuthenticationToken.
UsernamePasswordAuthenticationToken is just one of the implementations of the Authentication, which can be used in your case. When you use public constructor of this class with a collection of GrantedAuthority, your create a trusted (i.e. isAuthenticated() = true) authentication token. So security context will know that request is already authenticated.
By the way, constructor you use is intended to be called only by AuthenticationManager or AuthenticationProvider - that is stated in the javadoc.
For safe usage try static factory method:
return UsernamePasswordAuthenticationToken.authenticated(principal, credentials, userDetails.getAuthorities());
I use Spring Security with OAuth 2. My OAuth users have account_locked and enabled fields and I use them for different reasons. E.g. account_locked for blocking users and enabled for deactivating users. When user tries to log in and has block, then he receives 401 HTTP code and message "User account is locked". If he is deactivated, then he also receives 401 HTTP code and message "User is disabled".
I want to enhance these errors with additional information(like it is possible to enhance token using TokenEnhancer) to distinguish block code from deactivated code on the client. E.g. with the field reason_of_fail. Please note that I don't want to use text messages from these errors.
I already tried to use this(from this answer):
#Override
protected void configure(HttpSecurity http) throws Exception {
http.exceptionHandling()
.authencationEntryPoint(unauthorizedHandler)
.accessDeniedHandler(accessDeniedHandler);
}
But these handlers doesn't catch LockedException and DisabledException.
So, how I can enhance these errors with additional field(key-value pair)?
Authorization server
If you would like to add this functionality to your authorization server, and you have a custom AbstractTokenGranter (see conversation below),
then you can catch the desired exceptions in the getOAuth2Authentication method.
You can then throw a custom exception that extends OAuth2Exception and populates the additionalInformation map with any of the additional fields you require.
For an example of how to do this you can see the ResourceOwnerPasswordTokenGranter from the spring-security-oauth project.
Client
If, instead, you would like to add this functionality to your OAuth2 client, then you can use a custom AuthenticationFailureHandler to catch the LockedException and DisabledException.
Here is an example security configuration:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.oauth2Login()
.failureHandler(new CustomAuthenticationFailureHandler());
}
And an example of a custom AuthenticationFailureHandler:
public class CustomAuthenticationFailureHandler implements AuthenticationFailureHandler {
#Override
public void onAuthenticationFailure(HttpServletRequest request,
HttpServletResponse response, AuthenticationException exception) {
if (exception instanceof DisabledException) {
// throw new exception or modify response
}
}
}
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'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.
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.