I'm a newbie when talking about Spring Security, specially with JWT and CORS, so I apologise in advance if I don't speak clearly about the matter.
We were asked to make an application which simulates a private clinic website, on which patients can make an appointment with a doctor and buy products from the pharmacy. Doctors can introduce information in the database about their patients. Our project has a Restful API as well, which can be accessed through a mobile app (or Postman). What the API does is showing a list of products we have stored in the database.
All users can log in through a log in form, which uses Spring Security. On the other hand, if we wanted to retrieve the information of our API, CORS and JWT are used in addition to Spring Security.
The problem comes when I set up a custom authorization filter our teacher gave us to do this (I have commented the line that does this). We can access our API using Postman perfectly: we log in with the admin user and pass the authorization token to our API route, and in return we get the list of products. But when the filter is working, we can no longer use the log in form of our website to authenticate. The whole proccess goes like this:
- The application starts at the main page (localhost:8080/inicio).
- In the main page there is a 'Login' button which appears when the user is not authenticated . Clicking it takes us to the log in form.
- Once in the log in form (localhost:8080/auth/login) we fill all the fields neccesary for us to log in as an user from the database (in this case, username: admin, password: admin).
- We submit the form, which takes us to the petition in charge of the authentication proccess (localhost:8080/login/login-post).
- At the end of the proccess, we are redirected back to the main page. The "Login" button should appear as "Logout" when the user is authenticated. But it doesn't. We cannot navigate to other pages the authenticated user should have access to neither.
No error messages are provided by the console, and all it does is taking me back to the main page without having the user authenticated.
This is my Spring Security configuration class:
@Autowired
@Qualifier("userService")
private UserService userService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService).passwordEncoder(passwordEncoder());
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
// .addFilterAfter(new JWTAuthorizationFilter(), UsernamePasswordAuthenticationFilter.class)
.authorizeRequests()
.antMatchers("/", "/css/**", "/img/**", "/js/**", "/vendor/**", "/inicio/**", "/pacientes/altaPaciente/**", "/pacientes/addPaciente/**", "/auth/**").permitAll()
.antMatchers(HttpMethod.GET, "/authRest/**").permitAll()
.antMatchers(HttpMethod.POST, "/authRest/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/auth/login")
.defaultSuccessUrl("/inicio/", true)
.loginProcessingUrl("/auth/login-post")
.permitAll()
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/auth/login?logout")
.permitAll();
}
And my JWT Authorization filter:
private final String HEADER = "Authorization";
private final String PREFIX = "Bearer ";
private final String SECRET = "mySecretKey";
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
try {
if (checkJWTToken(request, response)) {
Claims claims = validateToken(request);
if (claims.get("authorities") != null) {
setUpSpringAuthentication(claims);
} else {
SecurityContextHolder.clearContext();
}
} else {
SecurityContextHolder.clearContext();
}
chain.doFilter(request, response);
} catch(ExpiredJwtException | UnsupportedJwtException | MalformedJwtException e) {
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
((HttpServletResponse) response).sendError(HttpServletResponse.SC_FORBIDDEN, e.getMessage());
return;
}
}
private Claims validateToken(HttpServletRequest request) {
String jwtToken = request.getHeader(HEADER).replace(PREFIX, "");
return Jwts.parser().setSigningKey(SECRET.getBytes()).parseClaimsJws(jwtToken).getBody();
}
private void setUpSpringAuthentication(Claims claims) {
@SuppressWarnings("unchecked")
List<String> authorities = (List<String>) claims.get("authorities");
UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(
claims.getSubject(),
null,
authorities.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList())
);
SecurityContextHolder.getContext().setAuthentication(auth);
}
private boolean checkJWTToken(HttpServletRequest request, HttpServletResponse res) {
String authenticationHeader = request.getHeader(HEADER);
if (authenticationHeader == null || !authenticationHeader.startsWith(PREFIX)) {
return false;
}
return true;
}
EDIT: As requested, here are the logs I get when I try to log in as an existing user in the database using the web form: https://pastebin.com/7SYX2MZF