I'm having the problem that when I call the post request: localhost:8080/authenticate
The security of my applications says it need and token. When the request is called, filters go over it, so this is not the intention. Now the security asks for a bearer token while this is the first request and that of course it is not present yet. I'm getting the error JWT Token does not begin with Bearer String
My configure method:
#Override
protected void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/authenticate");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.cors().disable()
.authorizeRequests()
.antMatchers("/authenticate").permitAll()
.antMatchers("/**/private/**").authenticated()
.anyRequest().permitAll()
.and()
.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class)
.exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint);
}
My filter method:
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
final String requestTokenHeader = request.getHeader("Authorization");
System.out.println("JWT Request: " + request.getRequestURI());
System.out.println("JWT Contain: " + request.getRequestURI().contains("authenticate"));
String username = null;
String jwtToken = null;
//Remove comment for second approach
if (request.getRequestURI().contains("authenticate") == false) {
System.out.println("Do Noting, Permit It");
chain.doFilter(request, response);
} else 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");
}
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = this.jwtUserDetailsService.loadUserByUsername(username);
if (jwtTokenUtil.validateToken(jwtToken, userDetails)) {
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
usernamePasswordAuthenticationToken
.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
}
}
chain.doFilter(request, response);
}
My controller class:
#RestController
#CrossOrigin
public class JwtAuthenticationController {
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private JwtTokenUtil jwtTokenUtil;
#Autowired
private JwtUserDetailsService userDetailsService;
#RequestMapping(value = "/authenticate", method = RequestMethod.POST)
public ResponseEntity<?> createAuthenticationToken(#RequestBody JwtRequest authenticationRequest) throws Exception {
authenticate(authenticationRequest.getUsername(), authenticationRequest.getPassword());
final UserDetails userDetails = userDetailsService
.loadUserByUsername(authenticationRequest.getUsername());
final String token = jwtTokenUtil.generateToken(userDetails);
return ResponseEntity.ok(new JwtResponse(token));
}
#RequestMapping(value = "/register", method = RequestMethod.POST)
public ResponseEntity<?> saveUser(#RequestBody UserDTO user) throws Exception {
return ResponseEntity.ok(userDetailsService.save(user));
}
private void authenticate(String username, String password) throws Exception {
try {
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
} catch (DisabledException e) {
throw new Exception("USER_DISABLED", e);
} catch (BadCredentialsException e) {
throw new Exception("INVALID_CREDENTIALS", e);
}
}
}
I want the program work so that when I send the localhost:8080/authenticate request there will be no filters, but when I call every other request there will be the filters to check if the token is present.
Thank You in advance.
Override the method configure(WebSecurity web) to ignore /authenticate endpoint so that it will not be included in Spring Security Filter Chain as below;
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/authenticate");
}
Related
I have following metod in controller:
#PostMapping(path = "/api/users/login", consumes = APPLICATION_JSON_VALUE, produces = APPLICATION_JSON_VALUE)
#ResponseStatus(OK)
public TokenResponse login(#RequestBody LoginUserRequest loginUserRequest, Principal principal) {
return new TokenResponse().setAccessToken("token");
}
here is a WebSecurityConfigurerAdapter
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.formLogin().disable()
.authorizeRequests().antMatchers("/api/users/login").permitAll()
.and()
.authorizeRequests().antMatchers("/api/**").authenticated()
.and()
.addFilterBefore(mobileAuthenticationFilter(objectMapper), UsernamePasswordAuthenticationFilter.class)
.addFilter(new JwtAuthorizationFilter(authenticationManager(), super.userDetailsService()));
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
auth.jdbcAuthentication().dataSource(dataSource)
.usersByUsernameQuery("SELECT login, pass, active FROM users WHERE login = ?")
.authoritiesByUsernameQuery("SELECT login, 'ROLE_USER' FROM users WHERE login = ?")
.passwordEncoder(new CustomPasswordEncoder());
}
#Bean
public MobileAuthenticationFilter mobileAuthenticationFilter(ObjectMapper objectMapper) throws Exception {
MobileAuthenticationFilter mobileAuthenticationFilter = new MobileAuthenticationFilter(objectMapper);
mobileAuthenticationFilter.setAuthenticationManager(authenticationManager());
mobileAuthenticationFilter.setAuthenticationSuccessHandler((request, response, authentication) -> {
System.out.println(request);
});
return mobileAuthenticationFilter;
}
MobileAuthenticationFilter is reading from json body and prepare UsernamePasswordAuthenticationToken
public class MobileAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
private final ObjectMapper objectMapper;
public MobileAuthenticationFilter(ObjectMapper objectMapper) {
super(new AntPathRequestMatcher("/api/users/login"));
this.objectMapper = objectMapper;
}
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
try {
BufferedReader reader = request.getReader();
StringBuilder sb = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
sb.append(line);
}
reader.mark(0);
LoginUserRequest loginUserRequest = objectMapper.readValue(sb.toString(), LoginUserRequest.class);
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(loginUserRequest.getLogin(), loginUserRequest.getPassword());
return getAuthenticationManager().authenticate(token);
} catch (IOException e) {
throw new IllegalArgumentException(e.getMessage());
}
}
}
this code works fine but is one thing which I want to archive.
After successfully authentication, response is produced immediately by the:
mobileAuthenticationFilter.setAuthenticationSuccessHandler((request, response, authentication) -> {
System.out.println(request);
});
Here ofcourse I can return something to client (in body), but there is any possibility to invoke controller method public TokenResponse login and that method should return a response (based on method contract and annotations for http code)?
This method in controller in that scenario is never called.
Would there be a formLogin, you could have used the successHandler(...) to redirect to the page you want. Note that you have to also think about error responses.
Since you have explicitly disabled formLogin, I recommend if users call /api/users/login instead of authenticating them in attemptAuthentication(...).
So, as you have put it ..addFilterBefore(mobileAuthenticationFilter(objectMapper), UsernamePasswordAuthenticationFilter.class), your filter will be triggered populating the resulting response.
Your controller will look like something like this:
public TokenResponse login(#Valid #RequestBody LoginUserRequest loginUserRequest) {
//may be check for AuthenticationException
try{
...
generateToken(loginUserRequest.getUserName(), loginUserRequest.getPassword());
...
} catch(AuthenticationException ex){
// status = HttpStatus.UNAUTHORIZED;
} catch (Exception ex) {
//status = HttpStatus.INTERNAL_SERVER_ERROR;
}
}
public String generateToken(String name, String password) {
try {
// check for null or empty
UsernamePasswordAuthenticationToken upToken = new UsernamePasswordAuthenticationToken(name, password, new ArrayList<>());
Authentication authentication = authenticationManager.authenticate(upToken);
// do whatever operations you need and return token
} catch (Exception ex) {
throw ex;
}
}
As access token expires, client sends GET request with /refresh endpoint,but it ended with 401-unauthorized.
I have configured this request in webConfig. so, it does not need authorization. I have passed access token in header of /refresh request.
Note: If I don't pass token in header of /refresh request, It worked fine.
JwtAuthenticationController.java:
#RestController
public class JwtAuthenticationController {
#RequestMapping(value = "/refresh", method = RequestMethod.GET)
public ResponseEntity<?> refreshAuthenticationToken(HttpServletRequest request) {
final String token = request.getHeader("Authorization");
final String username = jwtUtils.getUsernameFromToken(token);
final UserDetails user = userDetailsService.loadUserByUsername(username);
if ((user.getUsername()).equals(username) && jwtUtils.isTokenExpired(token)) {
final String refreshedToken = jwtUtils.refreshToken(token);
return ResponseEntity.ok(new JwtAuthenticationResponse(refreshedToken));
}
else {
return ResponseEntity.badRequest().body(null);
}
}
}
WebSecurityConfig.java
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.csrf().disable()
.exceptionHandling()
.antMatchers("/register","/refresh")
.permitAll()
.anyRequest().authenticated();
httpSecurity.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
httpSecurity.headers().cacheControl().disable();
}
}
JwtAuthenticationFilter.java
#Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
String authToken = request.getHeader(AUTHORIZATION_HEADER);
if (authToken != null && authToken.startsWith(BEARER_PREFIX)) {
try {
authToken = authToken.substring(BEARER_PREFIX_LENGTH);
username = jwtUtils.getUsernameFromToken(authToken);
}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");
}
}
JwtUtils.java
private Claims getClaimsFromToken(String token) {
return Jwts.parser()
.setSigningKey(secret)
.requireIssuer(issuer)
.parseClaimsJws(token)
.getBody();
}
error log
I am unable to find out why this happen? What is the solution to get new access token using client sent refresh token.
If I don't pass token in header of /refresh request, It worked fine.
This is probably because of the JwtAuthenticationFilter. I think you should exclude also the /refresh from the check, so:
if (!request.getRequestURI().contains("/refresh") {
if (authToken != null && authToken.startsWith(BEARER_PREFIX)) {
//same logic
}
}
I have prepared configuration for Spring security for API calls. It should verify JWT token provided in the request.
http.csrf().disable().authorizeRequests()
.antMatchers("/v2/api/**/*").authenticated().and()
.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class)
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
and my controller method
#PreAuthorize("hasAuthority('ROLE_USER')")
#PutMapping(value = "/v2/api/dashboard/projects")
public List<Projects> getProjects(Principal principal) {
return dashboardService.getProjects();
}
and by executing request I get
Resolved
[org.springframework.web.HttpRequestMethodNotSupportedException:
Request method 'PUT' not supported]
When I change it to GetMapping request is being handled properly.
After settings logs from logging.level.org.springframework.web=DEBUG I can see that PUT not supported is not returned from /v2/api/dashboard/projects but from '/forbidden' which for obvious reasons doesn't support such methods.
Further investigation with debuging jwtRequestFilter showed that filter is not even executed on PUT, PATCH or DELETE methods.
code of it:
#Component
public class JwtRequestFilter extends OncePerRequestFilter {
private static final Logger log = LoggerFactory.getLogger(JwtRequestFilter.class);
#Autowired
private JwtUserDetailsService jwtUserDetailsService;
#Autowired
private JwtUtils jwtTokenUtil;
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
String requestTokenHeader = "";
try{
requestTokenHeader = WebUtils.getCookie(request, "token").getValue();
} catch (NullPointerException ex ){}
String username = null;
String jwtToken = null;
if (requestTokenHeader != null && requestTokenHeader.contains(".")) {
jwtToken = requestTokenHeader;
try {
username = jwtTokenUtil.getUsernameFromToken(jwtToken);
} catch (IllegalArgumentException e) {
log.error("Unable to get JWT Token");
} catch (ExpiredJwtException e) {
log.error("JWT Token has expired");
}
} else {
logger.warn("JWT Token does not look like token");
}
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = this.jwtUserDetailsService.loadUserByUsername(username);
if (jwtTokenUtil.validateToken(jwtToken, userDetails)) {
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
usernamePasswordAuthenticationToken
.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
}
}
chain.doFilter(request, response);
}
}
Can anyone give me a hint how to make it works so PUT and PATCH methods would be filtered with given class?
I try to use JWT to secure my resource based on Spring Security to achieve the following:
1. Token invalid or expired, return 401.
2. Successfully authorized but have no right to reach some controllers. Then return 403.
Now there is something wrong with it. I throw BadCredentialsException in my customized AuthenticationProvider (named TokenAuthenticationProvider) while user fails to be authenticated. But it finally returns 403. What can I do to handle the exception and return 403 http code.
I tried to implement AuthenticationEntryPoint but it doesn't work.
And one another way to handle the exception is using customized filter to catch the Exception. But this way definitely doesn't work because even the http response doesn't show 500 BadCredentialsException. So there must be a place already catching this Exception and I can't understand.
TokenAuthenticationProvider.class
public class TokenAuthenticationProvider implements AuthenticationProvider {
UserService userService;
public TokenAuthenticationProvider(UserService userService) {
this.userService = userService;
}
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
throw new BadCredentialsException("hello");
}
#Override
public boolean supports(Class<?> aClass) {
System.out.println(aClass);
TokenAuthenticationProvider.class.isAssignableFrom(aClass);
return true;
}
}
WebSecurity.class
#EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter {
#Autowired
UserService userService;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/**")
.addFilterAfter(new TokenAuthenticationFilter(), BasicAuthenticationFilter.class)
.authorizeRequests()
.anyRequest().hasRole("API");
}
#Override
protected void configure(AuthenticationManagerBuilder auth){
auth.authenticationProvider(new TokenAuthenticationProvider(userService));
}
}
TokenAuthenticationFilter.class
public class TokenAuthenticationFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
SecurityContextHolder.getContext().setAuthentication(new TokenAuthentication("hello"));
filterChain.doFilter(httpServletRequest, httpServletResponse);
}
}
The above code has been simplified. Instead of following a normal process, I directly throw the BadCredentialsException. What can I do to handle this Exception and return 401 http code.
You need to implement two filters to control the JWT generated.
First Filter is to authenticate and send the JWT to the client when the authentication is successful.
public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private final AuthenticationManager authenticationManager;
public JWTAuthenticationFilter(AuthenticationManager authenticationManger) {
this.authenticationManager = authenticationManger;
}
#Override
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException {
try {
AuthenticationRequest authRequest = new ObjectMapper().readValue(request.getInputStream(),
AuthenticationRequest.class);
return authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(
authRequest.getUsername(), authRequest.getPassword(), new ArrayList<>()));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
#Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response,
FilterChain chain, Authentication auth) throws IOException {
Date expirationDate = DateUtil.getDateAddDays(new Date(), 1);
String token = Jwts.builder().setIssuedAt(new Date()).setIssuer(WebSecurity.ISSUER)
.setSubject(((ClientDetails)auth.getPrincipal()).getUsername())
.setExpiration(expirationDate)
.signWith(SignatureAlgorithm.HS512, HardCodeUtil.JWT_KEY).compact();
response.addHeader(WebSecurity.HEADER_AUTHORIZATION, WebSecurity.PREFIX_JWT + token);
response.addHeader(WebSecurity.HEADER_JWT_EXPIRATION_DATE, String.valueOf(expirationDate.getTime()));
ObjectMapper mapper = new ObjectMapper();
ClientExtraParams extraParams = new ClientExtraParams((byte)1);
String body = mapper.writeValueAsString(new ClientLoginResponse(((ClientDetails)auth.getPrincipal()).getClient(),
extraParams));
response.setContentType("application/json");
response.getWriter().write(body);
response.getWriter().flush();
response.getWriter().close();
}
}
The second Filter is to validate every JWT before access to the resources:
public class JWTAuthorizationFilter extends BasicAuthenticationFilter {
private static final Logger log = Logger.getLogger(JWTAuthorizationFilter.class.getName());
public JWTAuthorizationFilter(AuthenticationManager authManager) {
super(authManager);
}
#Override
protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain)
throws IOException, ServletException {
String header = req.getHeader(WebSecurity.HEADER_AUTHORIZATION);
if (header == null || !header.startsWith(WebSecurity.PREFIX_JWT)) {
chain.doFilter(req, res);
return;
}
try {
UsernamePasswordAuthenticationToken authentication = getAuthentication(req);
SecurityContextHolder.getContext().setAuthentication(authentication);
chain.doFilter(req, res);
}catch (SignatureException ex) {
log.log(Level.SEVERE, "JWT SIGNING INVALID");
}catch (MalformedJwtException ex) {
log.log(Level.SEVERE, "JWT STRUCTURE INVALID");
}catch (ExpiredJwtException ex) {
log.log(Level.SEVERE, "JWT EXPIRED");
GeneralResponse jwtInvalidResponse = new GeneralResponse(ErrorsEnum.JWT_EXPIRED);
ObjectMapper mapper = new ObjectMapper();
String body = mapper.writeValueAsString(jwtInvalidResponse);
res.setContentType("application/json");
res.getWriter().write(body);
res.getWriter().flush();
res.getWriter().close();
}catch (UnsupportedJwtException ex) {
log.log(Level.SEVERE, "JWT UNSUPPORTED");
}catch (IllegalArgumentException ex) {
log.log(Level.SEVERE, "ILLEGAL ARGUMENT JWT ENVIADO");
}
}
private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
String token = request.getHeader(WebSecurity.HEADER_AUTHORIZATION);
if (token != null) {
String user = Jwts.parser()
.setSigningKey(HardCodeUtil.JWT_KEY)
.parseClaimsJws(token.replace(WebSecurity.PREFIX_JWT, ""))
.getBody()
.getSubject();
if (user != null) {
return new UsernamePasswordAuthenticationToken(user, null, new ArrayList<>());
}
}
return null;
}
}
In your Spring configuration of HttpSecurity add these filters:
.and().addFilter(new JWTAuthenticationFilter(authenticationManager()))
.addFilter(new JWTAuthorizationFilter(authenticationManager()));
I implemented this using this library:
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
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