I have created this system secured using the Spring Security. The problem is that all the configurations I have set up, are not being run and the requests are not being intercepted to be checked if they have a valid token or not. I am not using Spring Boot, however I have used some of the Spring libraries.
WebSecurityConfig.java
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
#Autowired
private UserDetailsService jwtUserDetailsService;
#Autowired
private JwtRequestFilter jwtRequestFilter;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
// configure AuthenticationManager so that it knows from where to load
// user for matching credentials
// Use BCryptPasswordEncoder
auth.userDetailsService(jwtUserDetailsService).passwordEncoder(passwordEncoder());
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.httpBasic().disable().csrf().disable()
// dont authenticate this particular request
.authorizeRequests().antMatchers("/studentsystem2/login").permitAll().
// all other requests need to be authenticated
anyRequest().authenticated().and().
// make sure we use stateless session; session won't be used to
// store user's state.
exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint).and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
// Add a filter to validate the tokens with every request
httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
}
}
JwtRequestFilter.java
#Component
public class JwtRequestFilter extends OncePerRequestFilter {
#Autowired
private LoginService loginService;
#Autowired
private JwtTokenUtil jwtTokenUtil;
#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;
// 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 {
System.out.println("JWT Token does not begin with Bearer String");
}
// Once we get the token validate it.
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = loginService.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);
}
}
chain.doFilter(request, response);
}
}
Basically, whenever I send a request to any endpoint , it will be executed perfectly well without checking if it has authorization or not. It needs to intercept the requests and allow only the ones that provide the right tokens.
All the configurations are okay in the java class files. Although, for a non-Spring Boot application, you need to add the following configuration in the web.xml file of the application. That connects the stuff together.
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Related
What I'm trying to do is just authenticate in-memory default user using a custom authentication filter that parses a JSON payload that contain the username and the password.
SecurityConfig.java
package ali.yousef.authdemo.config.security;
#Configuration
#EnableWebSecurity
public class SecurityConfig
{
#Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception
{
AuthenticationManager authenticationManager = authenticationConfiguration.getAuthenticationManager();
return authenticationManager;
}
#Bean
PasswordEncoder passwordEncoder()
{
return new BCryptPasswordEncoder();
}
#Bean
public SecurityFilterChain filterChain(HttpSecurity http, AuthenticationManager authenticationManager) throws Exception
{
JsonUserPasswordAuthenticationFilter jsonUserPasswordAuthenticationFilter = new JsonUserPasswordAuthenticationFilter();
jsonUserPasswordAuthenticationFilter.setAuthenticationManager(authenticationManager);
http
.csrf().disable()
.formLogin().disable()
.addFilterAt(jsonUserPasswordAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
.authorizeHttpRequests()
.requestMatchers("/api/**").authenticated()
.anyRequest().permitAll();
return http.build();
}
}
JsonUserPasswordAuthenticationFilter.java
package ali.yousef.authdemo.config.security;
public class JsonUserPasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter
{
protected JsonUserPasswordAuthenticationFilter(AuthenticationManager authenticationManager)
{
this.setAuthenticationManager(authenticationManager);
}
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException
{
UsernamePasswordDto usernamePasswordDto;
try
{
usernamePasswordDto = new ObjectMapper().readValue(request.getInputStream(), UsernamePasswordDto.class);
System.out.println(usernamePasswordDto.toString());
}
catch (IOException ioe)
{
throw new AuthenticationServiceException(ioe.getMessage(), ioe);
}
UsernamePasswordAuthenticationToken authToken =
new UsernamePasswordAuthenticationToken(usernamePasswordDto.getUsername(), usernamePasswordDto.getPassword());
return this.getAuthenticationManager().authenticate(authToken);
}
}
TestController.java
#RestController
public class TestController
{
#GetMapping("/api/hello")
public String hello(Principal principal)
{
return "hello " + principal.getName();
}
}
When authenticating the default user it gets authenticated and return the home page but when I try to send a request to /api/hello it respond with 403.
EDIT:
I edited how I register the custom authentication filter. But the same problem is present. It seems like the security context gets cleared after successful authentication and I get anonymousUser from principal.
Extending a UsernamePassWordAuthenticationFilter brings in more customizations than you need to set up a custom auth filter.. see this post for more details why your filter is not getting called - link
You can achieve the same using OncePerRequestFilter as below -
#Component
public class JsonUserPasswordAuthenticationFilter extends OncePerRequestFilter
{
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
UsernamePasswordAuthenticationToken authToken = YOUR_LOGIC(); // new UsernamePasswordAuthenticationToken("test", "passwd",Collections.emptyList()); as you have no authorities empty list is important here...
SecurityContextHolder.getContext().setAuthentication(authToken);
filterChain.doFilter(request, response);
}
}
#Configuration
#EnableWebSecurity
public class SecurityConfig
{
#Bean
PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
#Bean
public SecurityFilterChain filterChain(HttpSecurity http, JsonUserPasswordAuthenticationFilter filter) throws Exception {
http
.csrf().disable()
.formLogin().disable()
.addFilterBefore(filter, UsernamePasswordAuthenticationFilter.class)
.authorizeHttpRequests(auth -> {
auth.requestMatchers(new AntPathRequestMatcher("/api/**")).authenticated()
.anyRequest().permitAll();
}) ;
return http.build();
}
When you send a request to /api/hello your filter’s attemptAuthentication() never takes action. This is because your custom implementation extends the UsernamePasswordAuthenticationFilter (which in its turn extends the AbstractAuthenticationProcessingFilter). The UsernamePasswordAuthenticationFilter, by default, is used for .formLogin authentication, and handles the default AntRequestMatcher "/login". Sending a request to /api/hello is an endpoint that is not handled by your filter. Since your security configuration requires that any /api/** endpoint should be authenticated, you receive the error. So, it should be made clear that an implementation of either UsernamePasswordAuthenticationFilter or AbstractAuthenticationProcessingFilter, is usually being used for Authenticating a user.
However, you can use/add your testing /api/hello endpoint to your filter and confirm that it works. For instance, you can override the default “/login” AntRequestMatcher from within your custom filter constructor, by using something like that (use the appropriate for you Http action, GET, POST, etc:
protected JsonUserPasswordAuthenticationFilter(AuthenticationManager authenticationManager) {
this.setAuthenticationManager(authenticationManager);
super.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/api/hello", "POST"));
}
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;
}
}
I am made a sample spring boot app implementing JWT token authentication which is working partially. That means it does not let the request access the endpoints until generating the token by sending user details using /login url. Once the token is received, the token is sent with a header called Authorization. So untill the first url all with this header, it does not allow to access endpoints. But after the 1st call I can access the enpoints without the Authorization header which contains the JWT token.
SecurityConfig.java
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final CustomUserDetailsService customUserDetailsService;
#Autowired
public SecurityConfig(CustomUserDetailsService customUserDetailsService) {
this.customUserDetailsService = customUserDetailsService;
System.out.println("from SecurityConfig constructor");
System.out.println(this.customUserDetailsService.loadUserByUsername("batman").getUsername());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
System.out.println("from configure");
http.cors().and().csrf().disable().authorizeRequests()
.antMatchers(HttpMethod.POST, "/sign_up").permitAll()
.antMatchers("/*/floor1/**").hasRole("USER")
.antMatchers("/*/floor2/**").hasRole("ADMIN")
.and()
.addFilter(new JwtAuthenticationFilter(authenticationManager()))
.addFilter(new JwtAuthorizationFilter(authenticationManager(), customUserDetailsService));
}
}
JwtAuthenticationFilter.java
public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private AuthenticationManager authenticationManager;
public JwtAuthenticationFilter(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
#Override
// {"username":"batman","password":"123"}
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
try {
System.out.println(">>>>> AuthenticationFilter: checking user credentials....");
ApplicationUser applicationUser = new ObjectMapper().readValue(request.getInputStream(), ApplicationUser.class);
return authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(applicationUser.getUsername(), applicationUser.getPassword()));
} catch (IOException e) {
System.out.println(">>>>> AuthenticationFilter: error in checking user credentials....");
throw new RuntimeException(e);
} catch (Exception e) {
System.out.println(">>>>> AuthenticationFilter: error in checking user credentials....");
throw new RuntimeException(e);
}
}
#Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
System.out.println(">>>>> AuthenticationFilter: successfulAuthentication creating token...");
ZonedDateTime expirationTimeUTC = ZonedDateTime.now(ZoneOffset.UTC).plus(SecurityConstants.EXPIRATION_TIME, ChronoUnit.MILLIS);
String token = Jwts.builder().setSubject(((User)authResult.getPrincipal()).getUsername())
.setExpiration(Date.from(expirationTimeUTC.toInstant()))
.signWith(SignatureAlgorithm.HS256, SecurityConstants.SECRET)
.compact();
response.getWriter().write(token);
response.addHeader(SecurityConstants.HEADER_STRING, SecurityConstants.TOKEN_PREFIX + token);
System.out.println(">>>>> AuthenticationFilter: successfulAuthentication token created and added to response");
}
}
JwtAuthorizationFilter.java
public class JwtAuthorizationFilter extends BasicAuthenticationFilter {
private final CustomUserDetailsService customUserDetailsService;
public JwtAuthorizationFilter(AuthenticationManager authenticationManager, CustomUserDetailsService customUserDetailsService) {
super(authenticationManager);
this.customUserDetailsService = customUserDetailsService;
}
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
String header = request.getHeader(SecurityConstants.HEADER_STRING);
System.out.println(">>>>> AuthorizationFilter doFilterInternal: checking the availability of toke header...");
if(header == null || !header.startsWith(SecurityConstants.TOKEN_PREFIX)){
System.out.println(">>>>> AuthorizationFilter doFilterInternal: header is null or not start with token prefix");
chain.doFilter(request, response);
return;
}
UsernamePasswordAuthenticationToken authenticationToken = getAuthenticationToken(request);
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
chain.doFilter(request, response);
}
private UsernamePasswordAuthenticationToken getAuthenticationToken(HttpServletRequest request){
System.out.println(">>>>> AuthorizationFilter UsernamePasswordAuthentication: validating the token...");
String token = request.getHeader(SecurityConstants.HEADER_STRING);
if(token == null){
System.out.println(">>>>> AuthorizationFilter UsernamePasswordAuthentication: error: token is null");
return null;
}
String username = Jwts.parser().setSigningKey(SecurityConstants.SECRET).parseClaimsJws(token.replace(SecurityConstants.TOKEN_PREFIX, "")).getBody().getSubject();
UserDetails userDetails = customUserDetailsService.loadUserByUsername(username);
ApplicationUser applicationUser = customUserDetailsService.loadApplicationUserByUsername(username);
return username != null ? new UsernamePasswordAuthenticationToken(applicationUser, null, userDetails.getAuthorities()) : null;
}
}
in JwtAuthorizationFilter.java it returns true where the token is check for null. So it is supposed to prevent accessing endpoints
and give an error to the client. But it does not. It allows the request to slip through the filter
and access the endpoint. Please help me if i am missing something here.
Complete sample project: https://github.com/xandar6/jwt
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.
I created a custom filter that fetches a token and then populates the authentication object with the token-related roles
#Component
public class TokenAuthenticationFilter extends GenericFilterBean {
#Autowired
private IAMUserDAO iamUserDAO;
#Autowired
CDBUserProfileDao cdbUserProfileDao;
#Autowired
IAMOAuth2Dao iamOAuth2DAO;
final static Logger logger = Logger.getLogger(TokenAuthenticationFilter.class.getCanonicalName());
#Override
public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain)
throws IOException, ServletException {
final HttpServletRequest httpRequest = (HttpServletRequest) request;
final String accessToken = httpRequest.getHeader("Authorization");
logger.info("Request with token " + accessToken + " intercepted for rba purpose");
if (!StringUtil.isBlank(accessToken)) {
ResponseEntity<String> tokenResponse = Utils.validateAccessToken(httpRequest, iamOAuth2DAO);
if (tokenResponse.getStatusCode().equals(HttpStatus.OK)) {
try {
UserProfiles userProfileResponse = cdbUserProfileDao.getCDBUserProfile(tokenResponse.getBody());
if (userProfileResponse != null) {
String action = iamUserDAO.getFbiFederatedAction(userProfileResponse.getEntid(),
userProfileResponse.getRoles().getRole());
if (!StringUtil.isBlank(action)) {
List<GrantedAuthority> authorities = Arrays.asList(action.split(",")).stream()
.map(s -> new SimpleGrantedAuthority(s)).collect(Collectors.toList());
final User user = new User("", "", true, true, true, true, authorities);
final UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
user, null, user.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
} catch (Exception e) {
logger.error("rba processing encounter an error " + e.getMessage());
}
}
}
logger.info("Exiting rba filter with token " + accessToken);
chain.doFilter(request, response);
}
}
I then added that filter to a springsecuritycontext as followed:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
#Bean
public FilterRegistrationBean filterRegistrationBean() {
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(new TokenAuthenticationFilter());
registrationBean.setEnabled(false);
return registrationBean;
}
#Override
protected void configure(final HttpSecurity http) throws Exception {
// Implementing Token based authentication in this filter
http.addFilterBefore(new TokenAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
http.authorizeRequests().antMatchers("/calendar/search", "/calendar/v2/search")
.access("hasRole('use-calendar') or hasRole('admin')").anyRequest().authenticated();
}
}
The application existed already and I just try to add the spring security layer. The spring security version is 4.2.3. After days trying to implement this, the TokenAuthenticationFilter is not loaded and consequently no request is filtered. Please help.
Since the application already existed before adding the Spring Security layer, I had to add the filter in the web.xml file in the below manner:
<filter>
<filter-name>tokenAuthenticationFilter</filter-name>
<filter-class>com.mycompany.authenticateb.config.TokenAuthenticationFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>tokenAuthenticationFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>