Spring Boot Security + Keycloak adapter 401 Response when token expires while antMatchers is permitAll() - spring-boot

When i login i receive access_token and refresh_token which i send in every HTTP request as headers to the Spring boot server.
Problem that i have is when token expires i get 401 Response when i try to access routes that are accessible without authentication, so result is i can't access /v1/users/current which checks if token is valid if not take refresh token and give me a new access token.
What Im trying to do now is make /v1/users/current accessible without any type of authorization/authentication so i can receive a new access token, and I'm not sure how to achieve that using KeycloakWebSecurityConfigurerAdapter.
I've tryed to use HandlerInterceptor but preHandle request that i receive is already been trough keycloak and i recieve simmilar error like 401.
Thanks in advance!
EDIT: When i use POSTMAN and send a get request to /v1/users/current without access token(No auth) and with refresh token in header i still get access to /v1/users/current route and get the new access token i need.
#KeycloakConfiguration
class ResourceServerConfiguration extends KeycloakWebSecurityConfigurerAdapter {
#Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("*"));
configuration.setAllowedMethods(Arrays.asList("GET", "POST"));
//the below three lines will add the relevant CORS response headers
configuration.addAllowedOrigin("*");
configuration.addAllowedHeader("*");
configuration.addAllowedMethod("*");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
#Bean
public ServletListenerRegistrationBean<HttpSessionEventPublisher> httpSessionEventPublisher() {
return new ServletListenerRegistrationBean<>(new HttpSessionEventPublisher());
}
#Autowired
public void configureGlobal(
AuthenticationManagerBuilder auth) throws Exception {
KeycloakAuthenticationProvider keycloakAuthenticationProvider
= keycloakAuthenticationProvider();
keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(
new SimpleAuthorityMapper());
auth.authenticationProvider(keycloakAuthenticationProvider);
}
#Bean
#Override
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
}
#Bean
public KeycloakConfigResolver KeycloakConfigResolver() {
return new KeycloakSpringBootConfigResolver();
}
#Override
public void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.cors().configurationSource(corsConfigurationSource())
.and()
.authorizeRequests().antMatchers(HttpMethod.GET,"/v1/users/current").permitAll().and()
.authorizeRequests()
.antMatchers(HttpMethod.POST,"/login").permitAll()
.antMatchers(HttpMethod.POST,"/register/admin").hasRole("admin")
.antMatchers(HttpMethod.POST, "/*")
.authenticated()
.anyRequest()
.permitAll();
}
/* #Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers(HttpMethod.GET,"/v1/users/current");
}*/
}

It seems that KeycloakAuthenticationProcessingFilter is checking each http request that has Authorization HTTP header, which means if you need to access a route without checking it you send it without Authorization headers.
I've changed my /v1/users/current to POST without header and I'm sending access_token and refresh_token in request body.

Related

Spring Boot REST API disable Form Login redirect

I have been banging my head against the wall for hours trying to figure out something that I would expect to work out of the box these days.
I am building an API with Spring Boot backend and I will create a react front end.
I only have one server so I dont need to use tokens. I want the same normal server side sessions and cookies.
I managed to get the Authentication to work but for some reason it keeps redirecting after success to the default / endpoint.
I really do not want this to happen and can't figure out why this is the case. I also can't find any decent resources on the internet of people that have encountered this issue.
I have seen a few videos where I have seen people handling the login in a Rest Controller end point rather than using filters. I assume this could work but then how would I implement session management?
Here is the code so far:
#Configuration
#EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter {
#Autowired
private AuthUserService authUserService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(authUserService);
}
#Bean
public CorsConfigurationSource corsConfigurationSource(){
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.setAllowedOrigins(Arrays.asList("http://localhost:3000"));
corsConfiguration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", corsConfiguration);
return source;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.mvcMatchers("/api/**").hasRole("AUTH_USER")
.mvcMatchers("/**").permitAll();
http.cors();
http.addFilterAfter(new CsrfHandlerFilter(), CsrfFilter.class);
AuthenticationFilter filter = new AuthenticationFilter();
filter.setAuthenticationManager(authenticationManager());
http.addFilterAt(filter, UsernamePasswordAuthenticationFilter.class);
}
#Bean
public PasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
}
Authentication Filter:
public class AuthenticationFilter extends UsernamePasswordAuthenticationFilter {
public AuthenticationFilter(){
super.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/login", "POST"));
}
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
System.out.println("Custom Authentication Filter fired!");
ObjectMapper mapper = new ObjectMapper();
Login login = new Login();
try {
login = mapper.readValue(request.getInputStream(), Login.class);
} catch (StreamReadException e) {
e.printStackTrace();
} catch (DatabindException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
login.getUsername(),
login.getPassword()
);
return this.getAuthenticationManager().authenticate(token);
}
}
Login Model class:
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
public class Login {
private String username;
private String password;
}
I want a normal server side session. I am not using JWT just because it is a JavaScript client. But all I want is for it to not redirect. Is this possible?
Any advice would be appreciated
There are a few ways to approach this, depending on your preference.
Certainly, you can stand up your own Spring MVC endpoint and set the SecurityContext yourself. Spring Security's SecurityContextPersistenceFilter will store the SecurityContext in an HttpSessionSecurityContextRepository by default, which induces the container to write a JSESSIONID session cookie that can be used on subsequent requests.
The main reason to go this route is if you want to have access to the MVC feature set when writing this endpoint.
One downside of this route is that Spring Security 6 will no longer save the security context for you when it comes to custom MVC endpoints, so you would need to be aware of that when upgrading.
HTTP Basic
That said, it doesn't seem like your requirements are so sophisticated that you can't use Spring Security's OOTB behavior.
One way to do this is with HTTP Basic. Note that for simplicity, I'll publish the SecurityFilterChain as a #Bean instead of using the now-deprecated WebSecurityConfigurerAdapter:
#Bean
SecurityFilterChain web(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authorize) -> authorize
.mvcMatchers("/api/**").hasRole("AUTH_USER")
.mvcMatchers("/**").permitAll()
)
.httpBasic(Customizer.withDefaults())
.cors(Customizer.witHDefaults())
.addFilterAfter(new CsrfHandlerFilter(), CsrfFilter.class);
return http.build();
}
This will allow you to send the username/password using the Authorization: Basic header. There's no need in this case for you to stand up anything custom. The filter chain will store the security
context in the session, and your Javascript can call endpoints using the JSESSIONID or by resending the username/password creds.
AuthenticationSuccessHandler
If for some reason you want to use form login (what your sample is customizing right now), instead of creating a custom filter, you can configure the existing form login filter with an AuthenticationSuccessHandler that does not redirect:
#Bean
SecurityFilterChain web(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authorize) -> authorize
.mvcMatchers("/api/**").hasRole("AUTH_USER")
.mvcMatchers("/**").permitAll()
)
.formLogin((form) -> form
.successHandler((request, response, authentication) ->
response.setStatusCode(200)
)
)
.cors(Customizer.witHDefaults())
.addFilterAfter(new CsrfHandlerFilter(), CsrfFilter.class);
return http.build();
}
Once again, the filter chain will save the subsequent UsernamePasswordAuthenticationToken to the session and issue a JSESSIONID for subsequent requests.

automate the OAuth2 refresh_token process with SpringBoot 2

I have a SpringBoot2 application, a MainApp as a resource-server, KeyCloak as AuthorizationServer and a maven module, which is related to the MainApp, as a OAuth2LoginClient.
In other words, in MavenModule I have the follow SecurityConfig:
#Configuration
#PropertySource("classpath:idm.properties")
public class Auth0Provider extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests(authorizeRequests ->
authorizeRequests
.requestMatchers(PROTECTED_URLS).authenticated()
.anyRequest().authenticated()
)
.oauth2Login().redirectionEndpoint().baseUri("/callback*");
http.csrf().disable();
}
private static final RequestMatcher PROTECTED_URLS = new OrRequestMatcher(
new AntPathRequestMatcher("/idmauth/**")
);
}
There is also a controller that intercepts the protected call:
#Value("${oauth.redirectURL}")
private String redirectURL;
#Autowired
private OAuth2AuthorizedClientService clientService;
#RequestMapping(method = RequestMethod.GET, path = "/redirect")
public RedirectView redirectWithUsingRedirectView(OAuth2AuthenticationToken oauthToken, RedirectAttributes attributes) {
OAuth2AuthorizedClient client =
clientService.loadAuthorizedClient(
oauthToken.getAuthorizedClientRegistrationId(),
oauthToken.getName());
String token = client.getAccessToken().getTokenValue();
attributes.addAttribute("jwt", token);
return new RedirectView(redirectURL);
}
This return the AccessToken to my frontend. Clearly in my idm.properties file I have the spring.oauth2.client.provider and spring.oauth2.client.registration info.
Now the MainApp is a SpringBoot2 WebApp with this simple SecurityConfig:
#EnableWebSecurity
#Configuration
public class Oauth2RestApiSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors()
.and()
.requestMatchers().antMatchers("/api/**")
.and()
.authorizeRequests().anyRequest().authenticated()
.and()
.oauth2ResourceServer().jwt();
}
}
And in it's application.properties just the line:
spring.security.oauth2.resourceserver.jwt.jwk-set-uri=https://<host>/protocol/openid-connect/certs
All works fine but, when the token expire, the only way I have currently found to refresh my token
is to manually do this HTTP-POST:
POST /auth/realms/<audience>/protocol/openid-connect/token HTTP/1.1
Host: <host>
Content-Type: application/x-www-form-urlencoded
Content-Length: 844
client_id=<my_client_id>
&client_secret=<my_client_secret>
&refresh_token=<refresh_token_previously_obtained>
&grant_type=refresh_token
Is there a better way to do this? Maybe inside the SecurityConfig or with a specific path inside spring.oauth2.x properties?
Note that refreshing an access token is done on the OAuth 2.0 client side.
This is done automatically by Spring Security if you have configured a WebClient to be used when requesting protected resources.
#Bean
WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {
ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
return WebClient.builder()
.apply(oauth2Client.oauth2Configuration())
.build();
}
When you have done so, the expired OAuth2AccessToken will be refreshed (or renewed) if an OAuth2AuthorizedClientProvider is available to perform the authorization.

Spring Security blocks POST requests despite SecurityConfig

I'm developing a REST API based on Spring Boot (spring-boot-starter-web) where I use Spring Security (spring-security-core e spring-security-config) to protect the different endpoints.
The authentication is done by using a local database that contains users with two different sets of roles: ADMIN andUSER. USER should be able toGET all API endpoints and POST to endpoints based onrouteA. ADMIN should be able to do the same asUSER plus POST andDELETE to endpoints based on `routeB
However the behavior I'm getting is that I can do GET requests to any endpoint but POST requests always return HTTP 403 Forbidden for either type of user - ADMIN and USER - which is not expected what I'm expecting based on my SecurityConfiguration.
Any ideas of what am I missing?
SecurityConfiguration.java
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private static final Logger logger = LoggerFactory.getLogger(SecurityConfiguration.class);
#Autowired
private RESTAuthenticationEntryPoint authenticationEntryPoint;
#Autowired
private DataSource dataSource;
#Override
public void configure(AuthenticationManagerBuilder builder) throws Exception {
logger.info("Using database as the authentication provider.");
builder.jdbcAuthentication().dataSource(dataSource).passwordEncoder(new BCryptPasswordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().
authorizeRequests().antMatchers(HttpMethod.GET, "/**").hasAnyRole("ADMIN", "USER")
.antMatchers(HttpMethod.POST, "/routeA/*").hasAnyRole("ADMIN", "USER")
.antMatchers(HttpMethod.POST, "/routeB/*").hasRole("ADMIN")
.antMatchers(HttpMethod.DELETE, "/routeB/*").hasRole("ADMIN").and().
requestCache().requestCache(new NullRequestCache()).and().
httpBasic().authenticationEntryPoint(authenticationEntryPoint).and().
cors();
}
#Bean
public CorsConfigurationSource corsConfigurationSource() {
final CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("*"));
configuration.setAllowedMethods(Arrays.asList("HEAD", "GET", "POST", "PUT", "DELETE", "PATCH"));
configuration.setAllowCredentials(true);
configuration.setAllowedHeaders(Arrays.asList("Authorization", "Cache-Control", "Content-Type"));
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
RouteBController .java
#RestController
public class RouteBController {
static final Logger logger = LoggerFactory.getLogger(RouteBController.class);
public RouteBController() { }
#RequestMapping(value = "routeB", produces = MediaType.APPLICATION_JSON_UTF8_VALUE, method = RequestMethod.GET)
public String getStuff() {
return "Got a hello world!";
}
#RequestMapping(value = "routeB", produces = MediaType.APPLICATION_JSON_UTF8_VALUE, method = RequestMethod.POST)
public String postStuff() {
return "Posted a hello world!";
}
}
RESTAuthenticationEntryPoint.java
#Component
public class RESTAuthenticationEntryPoint extends BasicAuthenticationEntryPoint {
#Override
public void afterPropertiesSet() throws Exception {
setRealmName("AppNameHere");
super.afterPropertiesSet();
}
}
BEFORE disabling the CSFR as a way of fixing this issue, please check the resources on Mohd Waseem's answer to better understand why it is important and to have an idea of how it can be properly set up. As RCaetano has said, CSFR is here to help us from attacks and it should not be disabled blindly.
Since this answer still explained the 2 issues on my original questions, I'll leave it as the marked answer to create awareness about possible issues with the CSFT and security routes but don't take it literally.
There were 2 issues in SecurityConfiguration.java that made it misbehave.
Although the 403 Forbidden error message didn't contain any message indication of why it was failing (see example below) it turns out it was due to having CSRF enabled. Disabling it allowed for POST and DELETE requests to be processed.
{
"timestamp": "2018-06-26T09:17:19.672+0000",
"status": 403,
"error": "Forbidden",
"message": "Forbidden",
"path": "/routeB"
}
Also the expression used in antMatched(HttpMethod, String) for RouteB was incorrect because /routeB/* expects it to have something after /. The correct configurtion is /routeB/** since more paths can be present (or not).
The corrected SecurityConfiguration.java is
#Override
protected void configure(HttpSecurity http) throws Exception {
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().
authorizeRequests().antMatchers(HttpMethod.GET, "/**").hasAnyRole("ADMIN", "USER")
.antMatchers(HttpMethod.POST, "/routeA/**").hasAnyRole("ADMIN", "USER")
.antMatchers(HttpMethod.POST, "/routeB/**").hasRole("ADMIN")
.antMatchers(HttpMethod.DELETE, "/routeB/**").hasRole("ADMIN").and().
requestCache().requestCache(new NullRequestCache()).and().
httpBasic().authenticationEntryPoint(authenticationEntryPoint).and().
cors().and().
csrf().disable();
}
Source: StackOverflow em Português
Cross-site request forgery is a web security vulnerability that allows an attacker to induce users to perform actions that they do not
intend to perform.
In your case disabling CSRF protection exposes user to this vulnerability.
Note: If it was pure Rest API with O-Auth protection then CSRF was not
needed. Should I use CSRF protection on Rest API endpoints?
But In your case when user logs in a session is created and cookie is returned in response and without CSRF token Attacker can exploit it and perform CSRF.
It wouldn't be a good idea to disable CSRF instead you can configure your app to return CSRF token in response headers and then use it in all your subsequent state changing calls.
Add this line of code in your SecurityConfiguration.java
// CSRF tokens handling
http.addFilterAfter(new CsrfTokenResponseHeaderBindingFilter(), CsrfFilter.class);
CsrfTokenResponseHeaderBindingFilter.java
public class CsrfTokenResponseHeaderBindingFilter extends OncePerRequestFilter {
protected static final String REQUEST_ATTRIBUTE_NAME = "_csrf";
protected static final String RESPONSE_HEADER_NAME = "X-CSRF-HEADER";
protected static final String RESPONSE_PARAM_NAME = "X-CSRF-PARAM";
protected static final String RESPONSE_TOKEN_NAME = "X-CSRF-TOKEN";
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, javax.servlet.FilterChain filterChain) throws ServletException, IOException {
CsrfToken token = (CsrfToken) request.getAttribute(REQUEST_ATTRIBUTE_NAME);
if (token != null) {
response.setHeader(RESPONSE_HEADER_NAME, token.getHeaderName());
response.setHeader(RESPONSE_PARAM_NAME, token.getParameterName());
response.setHeader(RESPONSE_TOKEN_NAME, token.getToken());
}
filterChain.doFilter(request, response);
}
}
Header Response form Server:
Note that we now have CSRF token in the header. This will not change untill the session expires.
Also read: Spring Security’s CSRF protection for REST services: the client side and the server side for better understanding.
It's simple CSRF enabled issue that doesn't allow POST requests. I faced the same problem here's the solution: (Explained)
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers(HttpMethod.POST,"/form").hasRole("ADMIN") // Specific api method request based on role.
.antMatchers("/home","/basic").permitAll() // permited urls to guest users(without login).
.anyRequest().authenticated()
.and()
.formLogin() // not specified form page to use default login page of spring security
.permitAll()
.and()
.logout().deleteCookies("JSESSIONID") // delete memory of browser after logout
.and()
.rememberMe().key("uniqueAndSecret"); // remember me check box enabled.
http.csrf().disable(); // ADD THIS CODE TO DISABLE CSRF IN PROJECT.**
}
Above code:
http.csrf().disable();
will solve the problem.

iron-ajax request results in cors issue with spring boot

Currently I face a CORS issue when doing an ajax call (with the iron-ajax element of Polymer 2) to my server, developed using Spring Boot 2.
Performing a post request to /login via Postman returns in the expected results, however, using a browser like Safari or Chrome results in the following error:
Failed to load http://localhost:8080/login: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8081' is therefore not allowed access. The response had HTTP status code 403.
My configuration in the back-end looks as follows:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/users").permitAll()
.antMatchers(HttpMethod.POST, "/login").permitAll()
.anyRequest().authenticated()
.and()
// We filter the api/login requests
.addFilterBefore(new JWTLoginFilter("/login", authenticationManager()),
UsernamePasswordAuthenticationFilter.class)
// And filter other requests to check the presence of JWT in header
.addFilterBefore(new JWTAuthenticationFilter(),
UsernamePasswordAuthenticationFilter.class);
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// Create a default account
auth.inMemoryAuthentication()
.passwordEncoder(NoOpPasswordEncoder.getInstance())
.withUser("admin")
.password("password")
.roles("ADMIN");
}
#Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("http://localhost"));
configuration.setAllowedMethods(Arrays.asList("GET","POST"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
The ajax call is set up as follows:
<iron-ajax
id="postLoginAjax"
method="post"
headers='{"access-control-allow-origin": "*"}' // Tried with and without
content-type="application/json"
handle-as"json"
on-touch="touch"
on-response="handleUserResponse"
on-error"handleUserError">
</iron-ajax>
Based on other SO posts I have implemented the Bean, however still not successful.
EDIT:
Also following the global cors settings suggested at https://spring.io/blog/2015/06/08/cors-support-in-spring-framework does not result in what I want. I assume because it is relying on mvc dependency, which I am not using.
Thanks in advance for your help,
Chris

Spring Boot Authorization Basic Header Never Changes

I am attempting to setup a very basic spring boot authenticated application. I am setting the Authorization header in the client and sending it to the backend. I can verify that the client is sending the correct header.
The backend receives the header correctly on the first attempt to login. However if the login credentials are incorrect subsequent requests retain whatever the header for the intial request was (caching it or something).
I am using Redis to Cache the session. My config is as follows:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired AuthenticationEntryPoint authenticationEntryPoint;
#Autowired CsrfTokenRepository csrfTokenRepository;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint)
.and()
.csrf()
.disable()
.authorizeRequests().antMatchers("**")
.permitAll()
.anyRequest()
.authenticated()
;
}
}
AuthenticationEntryPoint
public class AuthenticationEntryPointBean {
#Bean
AuthenticationEntryPoint authenticationEntryPoint() {
return new RestAuthenticationEntryPoint();
}
}
Any direction would be appreciated.
** Edit **
Adding cache settings
#Configuration
#EnableRedisHttpSession
public class HttpSessionConfig {
#Bean
public JedisConnectionFactory connectionFactory() {
return new JedisConnectionFactory(); // <2>
}
}
Also I am trying to invalidate cache but that doesn't seem to work
#CrossOrigin
#RequestMapping(value="/auth/login", method = RequestMethod.GET, produces="application/json")
public #ResponseBody String login(#RequestHeader(name = "authorization") String authorization, HttpSession session, HttpServletRequest request)
{
try
{
authorization = authorization.substring("Basic ".length());
String decoded = new String(Base64.getDecoder().decode(authorization),"UTF-8");
Gson gson = new Gson();
LoginRequest login = gson.fromJson(decoded,LoginRequest.class);
UserAuthenticationEntity entity = service.getSecurityContext(login).orElseThrow(() ->
new BadCredentialsException("Authentication Failed.")
);
session.setMaxInactiveInterval((int)TimeUnit.MINUTES.toSeconds(expiresInMinutes));
SecurityContextHolder.getContext().setAuthentication(new EntityContext(entity,expiresInMinutes));
String response = gson.toJson(BasicResponse.SUCCESS);
return response;
}
catch (Exception e)
{
session.invalidate();
e.printStackTrace();
throw new AuthenticationCredentialsNotFoundException("Authentication Error");
}
}
Adding the following to my web security config seemed to do the trick.
.requestCache()
.requestCache(new NullRequestCache())
I am not sure what side effects are of doing this. I picked it up off of a blog https://drissamri.be/blog/2015/05/21/spring-security-and-spring-session/
If there is any more insight into if this is good practice or bad practice I would appreciate any comments.
My final web security config looks like the following:
http
.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint)
.and()
.csrf()
.disable()
.authorizeRequests().antMatchers("**")
.permitAll()
.anyRequest()
.authenticated()
.and()
.requestCache()
.requestCache(new NullRequestCache())
.and()
.sessionManagement()
.sessionFixation()
.newSession()
;

Resources