Catch and enhance LockedException and DisabledException - java

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

Related

Spring Custom Access Denied Handler not working

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

Implementing user authentication in Spring MVC rest api

I have built a basic crud spring mvc rest api and would like to add basic authentication for validating the user and adding login and logout. I have found a tutorial which covers spring mvc.
I have tried implementing the basic authentication and the code is as follows:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
UserBuilder users = User.withDefaultPasswordEncoder();
auth.inMemoryAuthentication()
.withUser(users.username("marie").password("yourmarie").roles("USER"));
}
With the above code it provides authentication but when the credentials are invalid it returns an exception. Instead, I would like some sort of jason or message to be returned if possible so that I can process actions in the front end based off the returned value.
One the other hand if I try to access the endpoints without authentication, it gives me a login form. Here, I would like a response too.
Also, would like to add logout functionality as well.
Please guide.
Thanks in advance!
If you found an exception, while invalid credentials, then simply catch the exception in your controller. and create your own authentication exception and throw it with proper message.
catch (Exception ae) { throw new AuthenticationException("Invalid
credentials"); }
For logout you need to remove the token from the storage(memory) You can check token in request as below if
(StringUtils.hasText(httpServletRequest.getHeader("Authorization"))) {
String access_token = request.getHeader("Authorization"); }
https://www.baeldung.com/java-config-spring-security

When try to call /shutdown on springboot, requeries authorization header instead just correct shutdown application

I have app on spring-boot
added
endpoints.shutdown.enabled=true
endpoints.shutdown.sensitive=false
to application.properties
and try to call:
curl -i -X POST devapp583.netcracker.com:26810/shutdown
But requres Auth header instead just shutting down:
{"errorCode":403,"userMessage":"No 'Authorization' header","stacktrace":null,"remoteMessage":null}
It seems like a bug, take a look here. (If you use #EnableResourceServer)
According to the docs for 1.3 for health access restrictions, a
non-sensitive health endpoint should allow anonymous access. However,
this stops working if the #EnableResourceServer annotation is found.
When the OAuth2 resource server is enabled, even non-sensitive
endpoints require full authentication.
Also there you can find a workaround:
I did manage to work around the problem for the health endpoint by
adding the following bean definition.
#Bean
ResourceServerConfigurer resourceServerConfigurer() {
new ResourceServerConfigurerAdapter() {
#Override
void configure(ResourceServerSecurityConfigurer resources)
throws Exception {
resources.resourceId('blah')
}
#Override
void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
// allow anonymous access to health check endpoint
.antMatchers("/manage/health").permitAll()
// everything else requires authentication
.anyRequest().authenticated()
}
}
}
However I have not tried it myself

Spring session creation policy per-request?

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.

Spring Security 4 with custom provider and handlers not caching the url before authentication

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.

Categories