Adding parameters to header does not work while implementing SpringBoot Security JWT for REST API - spring-boot

I'm trying to implement authentication and authorization using JWT token in SpringBoot REST API.
In my JWTAuthentication class
#Override
protected void successfulAuthentication(HttpServletRequest req, HttpServletResponse res,
FilterChain chain, Authentication auth) throws IOException, ServletException {
String token = Jwts.builder().setSubject(((User) auth.getPrincipal()).getUsername())
.claim("roles", ((User) auth.getPrincipal()).getAuthorities())
.setExpiration(new Date(System.currentTimeMillis() + SecurityConstants.EXPIRATION_TIME))
.signWith(SignatureAlgorithm.HS512, SecurityConstants.SECRET.getBytes()).compact();
res.addHeader(SecurityConstants.HEADER_STRING, SecurityConstants.TOKEN_PREFIX + token);
chain.doFilter(req, res);
System.out.println("Token:"+token);
}
When I test my code by sending by posting the following message to 127.0.0.1:8080/login URL, I see that authentication is successful.
{"username":"admin", "password":"admin"}
And then Spring calls my JWT Authorization class
#Override
protected void doFilterInternal(
HttpServletRequest req, HttpServletResponse res, FilterChain chain)
throws IOException, ServletException {
String header = req.getHeader(SecurityConstants.HEADER_STRING);
if (header == null || !header.startsWith(SecurityConstants.TOKEN_PREFIX)) {
if (header == null) {
System.out.println("header null");
} else if (!header.startsWith(SecurityConstants.TOKEN_PREFIX)) {
System.out.println("token prefix missing in header");
}
chain.doFilter(req, res);
return;
}
UsernamePasswordAuthenticationToken authentication = getAuthentication(req);
SecurityContextHolder.getContext().setAuthentication(authentication);
chain.doFilter(req, res);
}
It prints the message: "token prefix missing in header"
Although I add the TOKEN_PREFIX in the successfulAuthentication method, it can not find it in the header in doFilterInternal method.
By the way, my security config is like this:
#EnableWebSecurity(debug = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired private UserDetailsService userDetailsService;
#Autowired private BCryptPasswordEncoder bCryptPasswordEncoder;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors()
.and()
.csrf()
.disable()
.authorizeRequests()
.antMatchers("/admin/**")
.hasRole("ADMIN")
.anyRequest()
.authenticated()
.and()
.addFilter(new JWTAuthenticationFilter(authenticationManager()))
.addFilter(new JWTAuthorizationFilter(authenticationManager()))
// this disables session creation on Spring Security
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers(
"/v2/api-docs",
"/configuration/ui",
"/swagger-resources/**",
"/configuration/security",
"/swagger-ui.html",
"/webjars/**");
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder);
}
#Bean
CorsConfigurationSource corsConfigurationSource() {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());
return source;
}
}
I checked the SpringBoot books but could not find a book that describes the inner details of the security framework. Since I did not understand how the framework works, I could not solve the problems by just looking at the blogs. Is there a book that you can suggest describing the details of SpringBoot Security?
Thanks

You set your token after you successfully authenticated the user to the header of
the Http response:
res.addHeader(SecurityConstants.HEADER_STRING, SecurityConstants.TOKEN_PREFIX + token);
The internal JWT filter (from what I understand in your question is called after yours), looks in the Http headers of the request
String header = req.getHeader(SecurityConstants.HEADER_STRING);
and there they are not present.
In general, the second filter should not be active after you authenticated a user and should just return the JWT token to the client. Any subsequent call of the client should then include the JWT token in the Authorization header using Bearer: YourJWTToken for calling e.g. protected APIs.

Related

How to handle OAuth2 and custom JWT authentication?

Currently my Spring Boot app authenticates users using JWT which it also generates:
Security config:
public SecurityFilterChain customFilterChain(HttpSecurity http) throws Exception {
return http
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeRequests(auth -> auth
.anyRequest().authenticated()
)
.userDetailsService(userService)
.addFilter(new AuthenticationFilter(authenticationManager(), userService))
.addFilterBefore(new AuthorizationFilter(), UsernamePasswordAuthenticationFilter.class)
.build();
}
AuthorizationFilter:
public class AuthorizationFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
if (!hasAuthorizationToken(request)) {
filterChain.doFilter(request, response);
return;
}
String authorizationHeader = request.getHeader(AUTHORIZATION);
try {
UsernamePasswordAuthenticationToken authToken = JwtTokenUtil.decodeJwtToken(authorizationHeader);
SecurityContextHolder.getContext().setAuthentication(authToken);
} catch (Exception ex) {
log.error("Authorization error: " + ex.getMessage());
response.sendError(HttpServletResponse.SC_FORBIDDEN);
}
filterChain.doFilter(request, response);
}
}
Now I would like to add OAuth2 auth in addition to existing one. I have added oauth2ResourceServer to the config:
public SecurityFilterChain customFilterChain(HttpSecurity http) throws Exception {
return http
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeRequests(auth -> auth
.anyRequest().authenticated()
)
.oauth2ResourceServer(oauth -> {
oauth
.jwt()
.jwtAuthenticationConverter(oAuthJwtAuthenticationConverter);
})
.userDetailsService(userService)
.addFilter(new AuthenticationFilter(authenticationManager(), userService))
.addFilterBefore(new AuthorizationFilter(), UsernamePasswordAuthenticationFilter.class)
.build();
}
Now when sending a request with OAuth2, it works well, but when sending my custom JWT I get an error
An error occurred while attempting to decode the Jwt: Signed JWT rejected: Another algorithm expected, or no matching key(s) found
How can I handle two types of JWT?

How to apply a security filter only on a restricted http path [duplicate]

This question already has answers here:
Disabling a filter for only a few paths in spring security
(2 answers)
Closed 1 year ago.
I have a problem with my Spring security configuration. I just want to basically apply an authentication filter to some paths, and not to other path. But the filter i have defined is applied on all the HTTP request ever what i write in the configuration.
Here is my code.
SecurityConfig:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private JwtFilter jwtFilter;
#Override
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user")
.password("{bcrypt}$2a$10$DmzAlIznZz3faNQx1eBTBOw6fNiGE105fKoHkvskYTMXH5OFUE6iy")
.roles("USER");
}
#Override
protected void configure(final HttpSecurity http) throws Exception {
http.csrf().disable()
.antMatcher("/admin/**")
.authorizeRequests() //
.anyRequest().authenticated() //
.and()
.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);
}
#Override
#Bean(name = BeanIds.AUTHENTICATION_MANAGER)
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
JWTFilter:
#Component
public class JwtFilter extends OncePerRequestFilter {
#Autowired
private JWTUtils jwtUtils;
#Override
protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException, ServletException, IOException {
String authorizationHeader = httpServletRequest.getHeader("Authorization");
String token = null;
String userName = null;
if (authorizationHeader != null) {
userName = jwtUtils.extractUsername(token);
}
if (jwtUtils.validateToken(token)) {
} else {
httpServletResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
return;
}
if (userName != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
new UsernamePasswordAuthenticationToken(userName, null);
usernamePasswordAuthenticationToken
.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest));
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
}
filterChain.doFilter(httpServletRequest, httpServletResponse);
}
}
For example if i try to call this servlet:
#PostMapping("/login")
public ResponseEntity<UserDetails> login(#RequestBody User user) throws Exception {
try {
Authentication authenticate = authenticate(user.getName(), user.getPassword());
UserDetails authenticatedUser = (UserDetails) authenticate.getPrincipal();
return ResponseEntity.ok()
.header(
HttpHeaders.AUTHORIZATION,
generateToken(authenticatedUser.getUsername())
)
.body(authenticatedUser);
} catch (BadCredentialsException e) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
}
My filter is called to verify if the client is authenticated but it is my login end point so my client is accordingly not authenticated yet...
For me the code I found on internet that should resolve this problem is this one:
#Override
protected void configure(final HttpSecurity http) throws Exception {
http.csrf().disable()
.antMatcher("/admin/**")
.authorizeRequests() //
.anyRequest().authenticated() //
.and()
.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);
}
You can find this code in the security config.
To exclude urls for the security filter, you should override the other configure method that accepts a WebSecurity as an argument and specify the url paths to ignore...
eg.
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers("/login/**");
}
Another option you could look into is to configure form based login in spring security...
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.anyRequest().authenticated()
.and()
.formLogin().loginPage("/login").permitAll()
.and()
...
.logout();
}
It also looks like you're custom coding oauth2 authentication. Have you looked at what spring security 5 provides out of the box for securing urls with jwt tokens?
Check out the documentation at
https://docs.spring.io/spring-security/site/docs/current/reference/html5/#oauth2resourceserver.

JWT Interceptor Springboot

I'd like to make people who hold the JWT can access all APIs but people can only access on EXCLUDE PATH now. what should I set up for that?
This is my WebConfig.
private static final String[] EXCLUDE_PATHS = {
"/api/user/**"
};
public void addInterceptors(InterceptorRegistry registry){
registry.addInterceptor(jwtInterceptor)
.addPathPatterns("/**")
.excludePathPatterns(EXCLUDE_PATHS);
This is my interceptor.
public class JwtInterceptor implements HandlerInterceptor {
private static final String HEADER_AUTH = "Authorization";
private final JwtTokenProvider jwtTokenProvider;
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
final String token = request.getHeader(HEADER_AUTH);
if(token !=null && jwtTokenProvider.validateToken(token)){
return true;
}else{
throw new UnauthorizedException();
}
this is my validateToken fn
public boolean validateToken(String jwtToken) {
try {
Jws<Claims> claims = Jwts.parser().setSigningKey(secretKey).parseClaimsJws(jwtToken);
return !claims.getBody().getExpiration().before(new Date());
} catch (Exception e) {
return false;
}
}
this is my doFilter
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
String token = jwtTokenProvider.resolveToken((HttpServletRequest) request);
if (token != null && jwtTokenProvider.validateToken(token)) {
Authentication authentication = jwtTokenProvider.getAuthentication(token);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
chain.doFilter(request, response);
}
This is my security Config.
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic().disable()
.csrf()
.ignoringAntMatchers("/h2-console/**")
.disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/h2-console/**").permitAll()
.antMatchers("/user/**").hasRole("USER")
.anyRequest().permitAll()
.and()
.addFilterBefore(new JwtAuthenticationFilter(jwtTokenProvider),
UsernamePasswordAuthenticationFilter.class);
}
Am I missing something? I add the Security Config.
You should use WebSecurity instead of interceptors.
Something like this for configuring which paths can be accessed and which cannot
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().authorizeRequests()
.antMatchers(HttpMethod.POST, SIGN_UP_URL).permitAll()
.anyRequest().authenticated()
.and()
.addFilter(new JWTAuthenticationFilter(authenticationManager()))
.addFilter(new JWTAuthorizationFilter(authenticationManager()))
// this disables session creation on Spring Security
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
This link should help you well.

Why doesn't SpringBoot Security return any response to REST client although the authentication is done

I'm trying to implement JWT auth with a REST API in SpringBoot. When I debug my code, I see that the JWT Authenticator works correctly but I can't see that the JWT Authorization code is called by the Spring Security framework and there's no response sent to my REST client. Below are some parts of my code that I think are related to my problem.
I think my request is getting lost somewhere in the Spring Security flow...
WebSecurityConfig:
#EnableWebSecurity(debug = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors()
.and()
.csrf()
.disable()
.authorizeRequests()
.antMatchers("/admin/**")
.hasRole("ADMIN")
.anyRequest()
.authenticated()
.and()
.addFilter(new JWTAuthenticationFilter(authenticationManager()))
.addFilter(new JWTAuthorizationFilter(authenticationManager()))
// this disables session creation on Spring Security
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
JWTAuthenticationFilter:
public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
public JWTAuthenticationFilter(AuthenticationManager authenticationManager) {
setAuthenticationManager(authenticationManager);
}
#Override
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException {
if (!HttpMethod.POST.matches(request.getMethod())) {
throw new AuthenticationServiceException(
"Authentication method not supported: " + request.getMethod());
}
try {
JsonAuthenticationParser auth =
new ObjectMapper().readValue(request.getInputStream(), JsonAuthenticationParser.class);
System.out.println(auth.username);
System.out.println(auth.password);
UsernamePasswordAuthenticationToken authRequest =
new UsernamePasswordAuthenticationToken(auth.username, auth.password);
return this.getAuthenticationManager().authenticate(authRequest);
} catch (Exception e) {
log.warn("Auth failed!!!!!!!!!!!!");
throw new InternalAuthenticationServiceException("Could not parse authentication payload");
}
}
#Override
protected void successfulAuthentication(HttpServletRequest req, HttpServletResponse res,
FilterChain chain, Authentication auth) throws IOException, ServletException {
String token = Jwts.builder().setSubject(((User) auth.getPrincipal()).getUsername())
.claim("roles", ((User) auth.getPrincipal()).getAuthorities())
.setExpiration(new Date(System.currentTimeMillis() + SecurityConstants.EXPIRATION_TIME))
.signWith(SignatureAlgorithm.HS512, SecurityConstants.SECRET.getBytes()).compact();
res.addHeader(SecurityConstants.HEADER_STRING, SecurityConstants.TOKEN_PREFIX + token);
System.out.println("Token:"+token);
}
JWTAuthorizationFilter
public class JWTAuthorizationFilter extends BasicAuthenticationFilter {
public JWTAuthorizationFilter(AuthenticationManager authManager) {
super(authManager);
}
#Override
protected void doFilterInternal(
HttpServletRequest req, HttpServletResponse res, FilterChain chain)
throws IOException, ServletException {
System.out.println("++++++++++++++++++++++++++++AUTHERIZATION doFilterInternal++++++++++++++++++++++");
}
private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
System.out.println("++++++++++++++++++++++++++++AUTHERIZATION getAuthentication++++++++++++++++++++++");
}
Background
When you add a filter to the filter chain without specifying the order (http.addFilter(...)), the comparator HttpSecurity uses to determine its order in the chain looks at the filter's parent class. UsernamePasswordAuthenticationFilter comes before BasicAuthenticationFilter (see FilterComparator).
The request comes in, reaches JWTAuthenticationFilter, and "ends" in the successfulAuthentication() method.
Solution
Continue the filter chain in JWTAuthenticationFilter:
#Override
protected void successfulAuthentication(HttpServletRequest req, HttpServletResponse res,
FilterChain chain, Authentication auth)
throws IOException, ServletException {
// ...
chain.doFilter(req, res);
}

I am getting null header value in spring security REST implementation

I am using jersey , spring boot and spring security to create rest web service.
Which will be consumed by angular 2 client.
Client is sending authorization header in request , But on server i am not receiving any header value. I am using jersey for web service resource also using spring security authentication and authorization.
Kindly help.
public class SecurityConfiguration extends WebSecurityConfigurerAdapter{
#Autowired
private CustomUserDetailsService userDetailService;
public SecurityConfiguration(CustomUserDetailsService userDetailService) {
this.userDetailService = userDetailService;
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/assets/**")
.and().ignoring().antMatchers("/app/**")
.and().ignoring().antMatchers("/opas/Payment/**") ;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.cors();
http.authorizeRequests()
.antMatchers(HttpMethod.POST, SIGN_UP_URL).permitAll()
.antMatchers(HttpMethod.POST, "/login").permitAll()
.and().authorizeRequests().antMatchers("/opas/common/**").permitAll()
.and().authorizeRequests().antMatchers("/opas/register/**").permitAll()
.anyRequest().authenticated()
.and()
.addFilterBefore(new CORSFilter(), ChannelProcessingFilter.class)
.addFilter(new JWTAuthenticationFilter(authenticationManager()))
.addFilter(new JWTAuthorizationFilter(authenticationManager()))
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailService);
}
#Bean
public CorsConfigurationSource corsConfigurationSource() {
final CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(ImmutableList.of("*"));
configuration.setAllowedMethods(ImmutableList.of("HEAD","GET", "POST", "PUT", "DELETE", "PATCH","OPTIONS"));
// setAllowCredentials(true) is important, otherwise:
// The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'.
configuration.setAllowCredentials(true);
// setAllowedHeaders is important! Without it, OPTIONS preflight request
// will fail with 403 Invalid CORS request
configuration.setAllowedHeaders(ImmutableList.of("Authorization", "Cache-Control", "Content-Type","X-Requested-With"));
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
I am getting null header value in following code
public class JWTAuthorizationFilter extends BasicAuthenticationFilter {
public JWTAuthorizationFilter(AuthenticationManager authManager) {
super(authManager);
}
#Override
protected void doFilterInternal(HttpServletRequest req,
HttpServletResponse res,
FilterChain chain) throws IOException, ServletException {
String header = req.getHeader(HEADER_STRING);
if (header == null || !header.startsWith(TOKEN_PREFIX)) {
chain.doFilter(req, res);
return;
}
try {
UsernamePasswordAuthenticationToken authentication = getAuthentication(req);
SecurityContextHolder.getContext().setAuthentication(authentication);
chain.doFilter(req, res);
}
catch (ExpiredJwtException eje) {
// TODO: handle exception
ResponseMessage responseMessage = new ResponseMessage();
responseMessage.setStatusCode(DomainConstants.FORBIDDEN_ERROR);
responseMessage.setMessage(DomainConstants.SESSION_EXPIRED);
Gson gson = new Gson();
res.getWriter().write(gson.toJson(responseMessage));
}
}
private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request)throws ExpiredJwtException {
String token = request.getHeader(HEADER_STRING);
if (token != null) {
// parse the token.
String user = Jwts.parser()
.setSigningKey(SECRET)
.parseClaimsJws(token.replace(TOKEN_PREFIX, ""))
.getBody()
.getSubject();
if (user != null) {
return new UsernamePasswordAuthenticationToken(user, null, new ArrayList<>());
}
return null;
}
return null;
}}
in latest versions of spring, if your header value equal to null, you get NullPointerException in spring security. maybe for your case you need to remove it with HttpServletResponseWrapper like this post

Resources