How to revoke auth token in spring security? - java

In logout controller I tryed to write a lot of combination of code. Now I have this:
final Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null) {
new SecurityContextLogoutHandler().logout(request, response, auth);
}
SecurityContextHolder.getContext().setAuthentication(null);
auth.setAuthenticated(false);
But after provided code execution token still valid.
What do I wrong? How to revoke token eventually?

The class you're looking for is
DefaultServices, method revokeToken(String tokenValue).
Here an exemple of a controller that revokes token, and here the oauth2 configuration with the DefaultServices bean.

If you need to revoke a token for another user than the current one (E.g. an admin wants to disable a user account), you can use this:
Collection<OAuth2AccessToken> tokens = tokenStore.findTokensByClientIdAndUserName(
"my_oauth_client_id",
user.getUsername());
for (OAuth2AccessToken token : tokens) {
consumerTokenServices.revokeToken(token.getValue());
}
With tokenStore being an org.springframework.security.oauth2.provider.token.TokenStore and consumerTokenServices being a org.springframework.security.oauth2.provider.token.ConsumerTokenServices

the thread is a bit old but for JWTToken users this is not working as the tokens are not stored.
So another option is to use a filter.
1 create a method for admin to lock/unlock a user on your database.
2 use a filter and if the method needs authentication check if the user is active or not
exemple :
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if(authentication != null
&& authentication.getName() != null
&& !authentication.getName().equalsIgnoreCase("anonymousUser")) {
UserModel user = userService.getUser(authentication.getName());
if(user != null && !user.isActivated())
throw new SecurityException("SECURITY_USER_DISABLED");
}
chain.doFilter(request, response);
}
On client side just intercept this error and disconnect user
hope this helps someone.

Simple example of token revocation for current authorized user using DefaultTokenServices:
Need Bean for Default token store
#Bean
public DefaultTokenServices tokenServices() {
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(tokenStore());
defaultTokenServices.setSupportRefreshToken(true);
return defaultTokenServices;
}
Then you can write your own controller
#RestController
#RequestMapping("/user")
public class UserApi {
#Autowired
private DefaultTokenServices tokenServices;
#Autowired
private TokenStore tokenStore;
#DeleteMapping("/logout")
#ResponseStatus(HttpStatus.NO_CONTENT)
public void revokeToken() {
final OAuth2Authentication auth = (OAuth2Authentication) SecurityContextHolder
.getContext().getAuthentication();
final String token = tokenStore.getAccessToken(auth).getValue();
tokenServices.revokeToken(token);
}
}

Autowire the DefaultTokenServices then use this code:
String authHeader = request.getHeader("Authorization");
String tokenValue = authHeader.replace("bearer", "").trim();
tokenService.revokeToken(tokenValue);
tokenService.setAccessTokenValiditySeconds(1);
tokenService.setRefreshTokenValiditySeconds(1);
Just try the code to revoke the access token.

Related

Do I need a separate JWT Filter for Multiple Logins?

The User login is working well but I want to add a Customer Module to the project. I know that I need to write a custom UserDetails class to get the customer Username but I want to ask if I need to write another Custom JWT filter for the Customer Login validation. Presently this is the Filter class that I have for User Login. I have added a username and password field to the Customer entity.
#Component
public class JwtRequestFilter extends OncePerRequestFilter {
#Autowired
private JwtTokenUtil jwtTokenUtil;
#Autowired
private UserAccountService myUserDetailsService;
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
final String requestTokenHeader = request.getHeader("Authorization");
String username = null;
String jwtToken = null;
if (requestTokenHeader != null) {
jwtToken = requestTokenHeader.substring(7);
try {
username = jwtTokenUtil.getUsernameFromToken(jwtToken);
} catch (IllegalArgumentException e) {
System.out.println("Unable to get JWT Token");
} catch (ExpiredJwtException e) {
System.out.println("JWT Token has expired");
}
}
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = this.myUserDetailsService.loadUserByUsername(username);
if (jwtTokenUtil.validateToken(jwtToken, userDetails)) {
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
String authorities = userDetails.getAuthorities().stream().map(GrantedAuthority::getAuthority)
.collect(Collectors.joining());
System.out.println("Authorities granted : " + authorities);
usernamePasswordAuthenticationToken
.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
}
else {
System.out.println("Not Valid Token");
}
}
chain.doFilter(request, response);
}
}
As you can see the Filter is using the custom UserDetails to verify the username . How do I add the Customer userdetails service to the filter ? This is my first multiple login project please be lenient with me.
Differentiate between user and customer while logging. Accordingly, call the different service to get user details. More can be found here.
Spring Security user authentication against customers and employee
How do I add the Customer userdetails service to the filter?: inject it as you did with UserAccountService. If you do this way, you're using 1 filter (and of course, this filter is in 1 SecurityFilterChain), you could basically implement your filter like: trying to validate your user by myUserDetailsService and if it's not successful, continue with myCustomerDetailsService.
For multiple login project. The second way you could do is using 2 SecurityFilterChain. UserJwtFilter for 1 SecurityFilterChain and CustomJwtFilter for 1 SecurityFilterChain for example. People usually do this way for different login mechanisms Basic, OAuth2, SAML2. E.g:
Basic Authentication:
#Configuration
#Order(2)
public class BasicAuthenticationFilterChain extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.requestMatchers()
.antMatchers("/login", "/logout")
.and()
OAuth2 Authentication:
#Configuration
#Order(3)
public class OAuth2AuthenticationFilterChain extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.requestMatchers()
.antMatchers("/oauth")
.and()
In this case when a request with "/login" it'll be directed to BasicAuthenticationFilterChain, and a request with "/oauth" will go to OAuth2AuthenticationFilterChain. About Order: the lower is the higher priority and once the request's processed with a SecurityFilterChain, it won't go to another SecurityFilterChain. You can implement your project this way.
Conclusion: There are a lot of ways you can implement your idea with spring security, it depends on your choice.
it looks to me like you already did.
#Autowired
private UserAccountService myUserDetailsService;
But I would suggest using a Constructor instead of #Autowired. Spring will fill in the constructor parameters just the same. This could be very slim when you use the lombok library as well.
Using a constructor also makes mocking this a bit easier for testing.
Updated as discussed in the comments:
#Log //another lombok thing
#RequiredArgsConstructor
#Component
public class JwtRequestFilter extends Filter{
private final JwtTokenUtil jwtTokenUtil;
private final UserAccountService myUserDetailsService;
private final CustomerAccountService myCustomerDetailsService;
private static final String AUTH_HEADER = "authorization";
#Override
protected void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws ServletException, IOException {
String tokenHeader = ((HttpServletRequest) request).getHeader(AUTH_HEADER);
if(hasValue(tokenHeader) && tokenHeader.toLowerCase().startsWith("bearer ")){
jwtToken = requestTokenHeader.substring(7);
String username;
String jwtToken;
try {
username = jwtTokenUtil.getUsernameFromToken(jwtToken);
if (uSecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = myUserDetailsService.loadUserByUsername(username);
if(isNull(userDetails)){
userDetails = myCustomerDetailsService.loadCustomerByUsername(username);
}
if (jwtTokenUtil.validateToken(jwtToken, userDetails)) {
var token = createSecurityToken(userDetails);
SecurityContextHolder.getContext().setAuthentication(token);
} else {
throw new RuntimeException("Not a Valid Token.");
}
} else {
log.info("Authorization already present");
}
} catch (IllegalArgumentException e) {
throw new("Unable to get JWT Token",e);
} catch (ExpiredJwtException e) {
throw new("JWT Token has expired",e);
}
} else {
throw new RuntimeException("No valid authorization header found.");
}
chain.doFilter(request, response);
}
private UsernamePasswordAuthenticationToken createSecurityToken(UserDetails userDetails){
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
log.info("Authorities granted : {}", userDetails.getAuthorities());
token.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
return token;
}
}

How can I mock Authentication in a Spring Boot when using an Exernal Authentication Source

I have a Spring Boot based app that us using ISAM Authentication from an external provider.
I have a rest/json endpoint /actuator/health that returns different data depending on if the user is authenticated or not.
How can I mock authentication during unit testing to ensure my configuration is correct?
In setup(), I've tried setting a token manually, and overriding AuthorizationService to return true.
#Before
public void setUp() throws Exception
{
mockMvc = webAppContextSetup(wac).apply(springSecurity()).build();
List roles = Arrays.asList(new SimpleGrantedAuthority("ROLE_USER"));
UsernamePasswordAuthenticationToken auth =
new UsernamePasswordAuthenticationToken("dave", "secret",
roles);
if (!auth.isAuthenticated()) { fail("NOT AUTHENTICATED!"); }
SecurityContextHolder.getContext().setAuthentication(auth);
//fake logged in
when(authorizationService.isCurrentUserAuthorized(anyString(),
anyString(),
ArgumentMatchers.any(ResourceType.class),
ArgumentMatchers.any(ActionType.class)))
.thenReturn(true);
}
However, when I run
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (!auth.isAuthenticated()) { fail("NOT AUTHENTICATED!"); }
UsernamePasswordAuthenticationToken authToken =
(UsernamePasswordAuthenticationToken)auth;
mockMvc.perform(get("/health_secure")
.principal(auth)
.header("Authorization", "Bearer " + token))
.andDo(print())
.andExpect(status().isOk())
.andExpect(forwardedUrl("/actuator/health"));
I get:
"error":"invalid_token","error_description":"Cannot convert access token to JSON"
Great answer right here. Since spring security does not create a request-scoped bean, create one yourself and inject it in places where you need it. This gives you opportunity to mock it yourself.
Something like this?:
#Service
#RequiredArgsConstructor
public class UserService {
public Principal getPrincipal() {
return SecurityContextHolder.getContext().getAuthentication();
}
public Optional<String> getCurrentLogin() {
Principal principal = getPrincipal();
if (principal == null)
return Optional.empty();
return Optional.ofNullable(principal.getName());
}
}
My application Authenticates with ISAM/OAuth2
I was hoping to just Override the AuthenticationManager or the OAuth2AuthenticationManager
I found a slightly lower level solution.
NOTE: I would love to see a solution at the AuthenticationManager level.
I had to override the ResourceServerTokenServices bean to mock my fake Auth Data.
I added a setAuth() method to my #Before method.
//"constants" for fake auth values
private static final String user = "MyUser";
private static final String token = "MyTokenString";
//TokenServices to validate/convert ISAM Bearer Token
#MockBean ResourceServerTokenServices tokenServices;
private Authentication setAuth() throws Exception
{
UsernamePasswordAuthenticationToken auth =
new UsernamePasswordAuthenticationToken(user, token,
Collections.emptyList());
if (!auth.isAuthenticated()) { fail("NOT AUTHENTICATED!"); }
SecurityContextHolder.getContext().setAuthentication(auth);
DefaultOAuth2AccessToken mockTokenImpl = new DefaultOAuth2AccessToken(user);
mockTokenImpl.setScope(Collections.singleton("authenticate:applications"));
mockTokenImpl.setTokenType("Bearer");
when(tokenServices.readAccessToken(anyString())).thenReturn(mockTokenImpl);
OAuth2Authentication oa2Auth = mock(OAuth2Authentication.class);
when(oa2Auth.getPrincipal()).thenReturn(user);
when(oa2Auth.getCredentials()).thenReturn(token);
when(oa2Auth.getUserAuthentication()).thenReturn(auth);
when(oa2Auth.isAuthenticated()).thenReturn(true);
OAuth2Request oa2Req = mock(OAuth2Request.class);
when(oa2Auth.getOAuth2Request()).thenReturn(oa2Req);
when(tokenServices.loadAuthentication(anyString())).thenReturn(oa2Auth);
return auth;
}
This Method Configures tokenServices to have all the necessary info to fake it's way through authenticate() with Springs OAuth2AuthenticationManager class.

Cannot get roles working in Spring Boot Security

I have spent days on trying to get ROLES working within Spring Security using JWT tokens. Authentication seems to work fine but authorization seems to still not be working.
My current setup is that I have...
an In Memory Database for Users (I have one single user called "lion", and one single role "KING")
an authenticate end point which authenticates the user, and returns the JWT in the response.
My WebCofig looks like this :
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/hello").permitAll()
.antMatchers("/authenticate").permitAll()
.anyRequest().authenticated()
.and().exceptionHandling()
.and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
}
I Basically want to say
anyone can access (/)
anyone can access "hello" and "authenticate"
My web controller looks like this :
#Controller
public class WebController {
#PreAuthorize("permitAll()")
#RequestMapping("/hello")
#ResponseBody
public String hello() {
return "Hello! ALL USERS logged in or not are allowed to see this!";
}
#PreAuthorize("hasRole('KING')")
#RequestMapping("/king")
#ResponseBody
public String king() {
return "Hello King";
}
#PreAuthorize("hasRole('ROLE_KING')")
#RequestMapping("/king2")
#ResponseBody
public String king2() {
return "Hello King2";
}
}
My web token returned via my authentication looks good (i think..). Decoded the body looks like this :
{
"sub": "lion",
"scopes": "KING",
"iat": 1580645802,
"exp": 1580663802
}
... and will allow me to access anywhere where I just need to be authenticated. "anyRequest().authenticated()".
However both king and king2 REST endpoints give me 403 - "Access Denied" even though my user has role KING (I tried both KING and ROLE_KING)
I feel like I am missing a piece of the puzzle.
My JWT Filter seems to look good too. The authentication object (..which is set in the security context handler like so : SecurityContextHolder.getContext().setAuthentication(authentication);) has exactly ONE SimpleGrantedAuthority with role = KING.
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
final String authorizationHeader = request.getHeader("Authorization");
String username = null;
String jwt = null;
if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
jwt = authorizationHeader.substring(7);
username = jwtUtil.extractUsername(jwt);
}
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
if (jwtUtil.validateToken(jwt, userDetails)) {
userDetails.getAuthorities(); //has exactly ONE SimpleGrantedAuthority with value "KING"
SimpleGrantedAuthority s = new SimpleGrantedAuthority("asfd");
s.getAuthority(); //string
UsernamePasswordAuthenticationToken authentication = jwtUtil.getAuthentication(jwt,
SecurityContextHolder.getContext().getAuthentication(), userDetails);
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
logger.info("authenticated user " + username + ", setting security context");
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
chain.doFilter(request, response);
}
My UserDetailsService looks like this :
public class UserService implements UserDetailsService {
#Autowired
private UserRepository repository;
#Autowired
private PasswordValidationService passwordValidationService;
#Override
public User loadUserByUsername(String userName) throws UsernameNotFoundException {
UserEntity u = repository.findByUserNameIgnoreCase(userName);
SimpleGrantedAuthority a = new SimpleGrantedAuthority(u.getRole().toString());
ArrayList<GrantedAuthority> auths = new ArrayList<GrantedAuthority>();
auths.add(a);
return new User(u.getUserName(), u.getPassword(), auths);
}
}
Everything looks good to me which is why I am confused right now :(
*********** UPDATE ************
If I use hasAuthority instead of hasRole then this works :
#PreAuthorize("hasAuthority('KING')")
#RequestMapping("/king3")
#ResponseBody
public String king3() {
return "Hello King";
}
So how do I get hasRole to work? Am I missing the appending of "ROLE_" somewhere in my code?

Spring Security with JWT token is loading spring UserDetails object on every request

I am learning spring security with JWT token and spring boot. I have implemented it properly and it is working fine. But I have one doubt in how JwtRequestFilter works. I have gone through couple of websites to understand spring security with spring boot and found same thing. So let me go to main doubt.
I am adding JwtRequestFilter file below.
JwtRequestFilter.java
#Component
public class JwtRequestFilter extends OncePerRequestFilter {
#Autowired
private JwtUserDetailsService jwtUserDetailsService;
#Autowired
private JwtTokenUtil jwtTokenUtil;
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
final String requestTokenHeader = request.getHeader("Authorization");
String username = null;
String jwtToken = null;
// JWT Token is in the form "Bearer token". Remove Bearer word and get
// only the Token
if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer ")) {
jwtToken = requestTokenHeader.substring(7);
try {
username = jwtTokenUtil.getUsernameFromToken(jwtToken);
} catch (IllegalArgumentException e) {
System.out.println("Unable to get JWT Token");
} catch (ExpiredJwtException e) {
System.out.println("JWT Token has expired");
}
} else {
logger.warn("JWT Token does not begin with Bearer String");
}
// Once we get the token validate it.
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
// This below line is calling on every request
UserDetails userDetails = this.jwtUserDetailsService.loadUserByUsername(username);
// if token is valid configure Spring Security to manually set
// authentication
if (jwtTokenUtil.validateToken(jwtToken, userDetails)) {
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
usernamePasswordAuthenticationToken
.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
// After setting the Authentication in the context, we specify
// that the current user is authenticated. So it passes the
// Spring Security Configurations successfully.
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
}
}
filterChain.doFilter(request, response);
}
}
As highlighed to validate token we have to provide spring UserDetails object and we are getting spring UserDetails object from jwtUserDetailsService. So every request this filter will call then token verification will perform and we have to call jwtUserDetailsService on every request.
My doubt is inside my jwtUserDetailsService I am adding couple of validation and adding user privileges. So on every request below steps are repeated in jwtUserDetailsService.
Get user using username from DB.
Get user role
Get user privileges from DB.
Assign privileges to userDetails.
JwtUserDetailsService.java
#Service("jwtUserDetailsService")
#Transactional
public class JwtUserDetailsService implements UserDetailsService {
#Autowired
private UserRepository userRepository;
#Autowired
private IUserService service;
#Autowired
private MessageSource messages;
#Autowired
private RoleRepository roleRepository;
#Override
public UserDetails loadUserByUsername(String email)
throws UsernameNotFoundException {
User user = userRepository.findByEmail(email);
if (user == null) {
return new org.springframework.security.core.userdetails.User(
" ", " ", true, true, true, true,
getAuthorities(Arrays.asList(
roleRepository.findByName("ROLE_USER"))));
}
return new org.springframework.security.core.userdetails.User(
user.getEmail(), user.getPassword(), user.isEnabled(), true, true,
true, getAuthorities(user.getRoles()));
}
private Collection<? extends GrantedAuthority> getAuthorities(
Collection<Role> roles) {
return getGrantedAuthorities(getPrivileges(roles));
}
private List<String> getPrivileges(Collection<Role> roles) {
List<String> privileges = new ArrayList<>();
List<Privilege> collection = new ArrayList<>();
for (Role role : roles) {
collection.addAll(role.getPrivileges());
}
for (Privilege item : collection) {
privileges.add(item.getName());
}
return privileges;
}
private List<GrantedAuthority> getGrantedAuthorities(List<String> privileges) {
List<GrantedAuthority> authorities = new ArrayList<>();
for (String privilege : privileges) {
authorities.add(new SimpleGrantedAuthority(privilege));
}
return authorities;
}
}
So on every request these queries are executing. Is there any better way of doing this? Because once I am adding user privileges in spring UserDetails object why we need to do that again on every request. Or those have scope of request only. I have worked on spring mvc and once we add privileges in spring UserDetails object it will be there until I am hitting logout means It will be there in spring security context until we remove it. Will it be same in spring boot? If I am adding role and privileges details once in spring UserDetails object why we need to add it again?
So every request this filter will call then token verification will
perform and we have to call jwtUserDetailsService on every request.
This can't be correct since you have a condition if (SecurityContextHolder.getContext().getAuthentication() == null).
So the first time the token was validated, you query your user details service, fetch all grants and set them to the Security context (you are already doing it: SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);).
Furthermore, with the JWT auth you usually don't even need to access any user details service since all grants ideally should be contained in the token itself. So the only thing you need to do is validating token's signature.
Once an user logs in his authentication is established, so you don't need to do db call again, after login in every request user should be checked for authorization only with the roles being set in the token during authentication, you need to validate the token is not tampered in every request
instead of creating userdetails by loading user detail from db call
UserDetails userDetails = this.jwtUserDetailsService.loadUserByUsername(username);
you could also encode the user's username and roles inside JWT claims
and create the UserDetails object by parsing those claims from the JWT.

Spring boot security cannot log in after invalid credentials

I have problem with validating user credentials. When I give correct credentials first time everything goes OK but giving invalid credentials first and then give correct ones I get invalid credentials error. I use Postman Basic
Auth.
My config class:
#Configuration
#EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserService userService;
#Autowired
private CustomAuthenticationEntryPoint authenticationEntryPoint;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable().authorizeRequests()
.antMatchers(HttpMethod.POST ,"/login").permitAll()
.antMatchers("/admin").hasAuthority("ADMIN")
.anyRequest().authenticated().and().exceptionHandling().authenticationEntryPoint(authenticationEntryPoint).and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.ALWAYS).and()
.logout()
.deleteCookies("remove")
.invalidateHttpSession(true);
http.rememberMe().disable();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(this.userService)
.and().eraseCredentials(true);
}
#Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
And my controller class
#PostMapping
public ResponseEntity<?> loginButtonClicked(HttpServletRequest request) {
HttpSession session = request.getSession();
final String authorization = request.getHeader("Authorization");
String[] authorizationData=null;
if (authorization != null && authorization.startsWith("Basic")) {
// Authorization: Basic base64credentials
String base64Credentials = authorization.substring("Basic" .length()).trim();
String credentials = new String(Base64.getDecoder().decode(base64Credentials),
Charset.forName("UTF-8"));
// credentials = username:password
authorizationData = credentials.split(":", 2);
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(authorizationData[0], authorizationData[1],Arrays.asList(new SimpleGrantedAuthority("USER")));
User user = userService.findUserEntityByLogin(authorizationData[0]);
if(user != null && user.getFromWhenAcceptLoginAttempts() != null && (user.getFromWhenAcceptLoginAttempts()).isBefore(LocalDateTime.now())){
// Authenticate the user
Authentication authentication = authenticationManager.authenticate(authRequest);
SecurityContext securityContext = SecurityContextHolder.getContext();
securityContext.setAuthentication(authentication);
// Create a new session and add the security context.
session = request.getSession();
session.setAttribute("SPRING_SECURITY_CONTEXT", securityContext);
return new ResponseEntity<>(new LoginResponseObject(200,"ACCESS GRANTED. YOU HAVE BEEN AUTHENTICATED"), HttpStatus.OK);
}else{
session.getId();
SecurityContextHolder.clearContext();
if(session != null) {
session.invalidate();
}
return new ResponseEntity<>(new ErrorObject(403,"TOO MANY LOGIN REQUESTS","YOU HAVE ENTERED TOO MANY WRONG CREDENTIALS. YOUR ACCOUNT HAS BEEN BLOCKED FOR 15 MINUTES.", "/login"), HttpStatus.FORBIDDEN);
}
}else{
session.getId();
SecurityContextHolder.clearContext();
if(session != null) {
session.invalidate();
}
return new ResponseEntity<>(new ErrorObject(401,"INVALID DATA","YOU HAVE ENTERED WRONG USERNAME/PASSWORD CREDENTIALS", "/login"), HttpStatus.UNAUTHORIZED);
}
}
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public ObjectMapper objectMapper(){
return new ObjectMapper();
}
#Bean
public HttpSessionEventPublisher httpSessionEventPublisher() {
return new HttpSessionEventPublisher();
}
The problem is that the request is stored in cache due to your sessionCreationPolicy.
To avoid this problem, you could add .requestCache().requestCache(new NullRequestCache()) in your http security config to override the default request cache configuration, but be careful because this could create another side effect (it depends on your application).
In case you do not need the session, you can choose another session policy:
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).
Another alternative is to relay in Spring's BasicAuthenticationFilter. This filter does all the authentication logic for you. To enable it, you only have to add .httpBasic()in your http security configuration.
You may want to add a custom logic on authentication success/failure. In that case, you only have to create a custom filter (CustomBasicAuthenticationFilter) that extends BasicAuthenticationFilter class and overrides the methods onSuccessfulAuthentication()and onUnsuccessfulAuthentication(). You will not need to add .httpBasic() but you will need to insert your custom filter in the correct place:
.addFilterAfter(new CustomBasicAuthenticationFilter(authenticationManager), LogoutFilter.class).
Any of that 3 solutions will avoid your problem.
Try to write .deleteCookies("JSESSONID") in your SpringSecurityConfig class.

Categories