How to get out of static context in spring boot - spring-boot

Both methods, "createToken" and "getAuthentication" return errors when being used in class "JWTAuthenticationFilter" and "JWTAuthorizationFilter". The errors are Non-static method 'getAuthentication(java.lang.String)' cannot be referenced from a static context
The problem is that I don't have a single static field in all my code, so I don't understand the error. I also can't just declare the methods as static because I need to use the variable "jwt_secret" and in my previous post someone explained why I shouldn't make it static:
"Spring will not inject (autowire) into static fields.; that wouldn't make any sense even if it could. Spring beans are instances of classes, but static fields are not associated to any one instance. There are some ugly workarounds but better would be to eliminate the use of static fields."
TokenUtils
package com.XX.ZZ.security;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.stereotype.Component;
import java.util.*;
#Component
public class TokenUtils {
#Autowired
#Value("${custom.data.jwt.secret}")
private final String jwt_secret;
public TokenUtils(String jwtSecret) {
jwt_secret = jwtSecret;
}
public String createToken(String name, String email){
long expirationTime = 86400 * 1000;
Date expirationDate = new Date(System.currentTimeMillis() + expirationTime);
Map<String, Object> extra = new HashMap<>();
extra.put("name", name);
return Jwts.builder()
.setSubject(email)
.setExpiration(expirationDate)
.addClaims(extra)
.signWith(Keys.hmacShaKeyFor(jwt_secret.getBytes()))
.compact();
}
public UsernamePasswordAuthenticationToken getAuthentication(String token){
try {
Claims claims = Jwts.parserBuilder()
.setSigningKey(jwt_secret.getBytes())
.build()
.parseClaimsJws(token)
.getBody();
String email = claims.getSubject();
return new UsernamePasswordAuthenticationToken(email, null, Collections.emptyList());
} catch (JwtException e){
return null;
}
}
}
JWTAuthorizationFilter, error in UsernamePasswordAuthenticationToken usernamePAT = TokenUtils.getAuthentication(token)
package com.XX.ZZ.security;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import java.io.IOException;
#Component
public class JWTAuthorizationFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
String bearerToken = request.getHeader("Authorization");
if(bearerToken != null && bearerToken.startsWith("Bearer ")){
String token = bearerToken.replace("Bearer ", "");
UsernamePasswordAuthenticationToken usernamePAT = TokenUtils.getAuthentication(token);
SecurityContextHolder.getContext().setAuthentication(usernamePAT);
}
filterChain.doFilter(request, response);
}
}
JwtAuthenticationFilter, error in String token = TokenUtils.createToken
package com.XX.ZZ.security;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import java.io.IOException;
import java.util.Collections;
public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
#Override
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException {
AuthCredentials authCredentials = new AuthCredentials();
try {
authCredentials = new ObjectMapper().readValue(request.getReader(), AuthCredentials.class);
} catch (IOException e){ }
UsernamePasswordAuthenticationToken usernamePAT = new UsernamePasswordAuthenticationToken(
authCredentials.getEmail(),
authCredentials.getPassword(),
Collections.emptyList()
);
return getAuthenticationManager().authenticate(usernamePAT);
}
#Override
protected void successfulAuthentication(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain,
Authentication authResult) throws IOException, ServletException {
UserDetailsImpl userDetails = (UserDetailsImpl) authResult.getPrincipal();
String token = TokenUtils.createToken(userDetails.getName(), userDetails.getUsername());
response.addHeader("Authorization", "Bearer " + token);
response.getWriter().flush();
super.successfulAuthentication(request, response, chain, authResult);
}
}

By calling it as TokenUtils.getAuthentication(token) you are literally calling it as as if it is a static. You need an instance in JWTAuthenticationFilter:
TokenUtils tokenUtilsInstance = new TokenUtils("my secret");
tokenUtilsInstance.getAuthentication(token)
You probably need to drop the constructor TokenUtils(String jwtSecret). That will allow the following in JWTAuthenticationFilter. Much better Spring practice.
#Autowired
TokenUtils tokenUtilsInstance;

Related

Spring security with JWT when authorizing via telegram

I can’t correctly authorize through the telegram token, I seem to have done everything, but when I request I get a 403 error, although the context is correct
I don’t understand what the problem is, the context is authorized and there are roles, everything is configured, but it returns 403
SecurityContextImpl [Authentication=UsernamePasswordAuthenticationToken [Principal=ru.alishev.springcourse.FirstSecurityApp.security.TelegramUserDetails#3e264c5e, Credentials=[PROTECTED], Authenticated=true, Details=null, Granted Authorities=[Include0]]]
SecurityConfig:
package ru.alishev.springcourse.FirstSecurityApp.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import ru.alishev.springcourse.FirstSecurityApp.services.TelegramDetailService;
/**
* #author Neil Alishev
*/
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final TelegramDetailService telegramDetailService;
private final JWTFilter jwtFilter;
#Autowired
public SecurityConfig(TelegramDetailService telegramDetailService, JWTFilter jwtFilter) {
this.telegramDetailService = telegramDetailService;
this.jwtFilter = jwtFilter;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
// конфигурируем сам Spring Security
// конфигурируем авторизацию
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/api/tg_users/auth/telegram").hasRole("Include0")
.antMatchers("/authh/login", "/authh/registration", "/error", "/api/tg_users", "api/tg_users/auth/telegram").permitAll()
.anyRequest().authenticated()
.and()
.formLogin().loginPage("/api/tg_users")
.loginProcessingUrl("/process_login")
.defaultSuccessUrl("/hello", true)
.failureUrl("/auth/login?error")
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/auth/login")
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);
}
// Настраиваем аутентификацию
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(telegramDetailService)
.passwordEncoder(getPasswordEncoder());
}
#Bean
public PasswordEncoder getPasswordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
JWTFilter:
package ru.alishev.springcourse.FirstSecurityApp.config;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.JWTVerifier;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import ru.alishev.springcourse.FirstSecurityApp.security.JWTUtil;
import ru.alishev.springcourse.FirstSecurityApp.security.TelegramUserDetails;
import ru.alishev.springcourse.FirstSecurityApp.services.TelegramDetailService;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
#Component
public class JWTFilter extends OncePerRequestFilter {
private final JWTUtil jwtUtil;
private final TelegramDetailService telegramDetailService;
#Autowired
public JWTFilter(JWTUtil jwtUtil, TelegramDetailService telegramDetailService) {
this.jwtUtil = jwtUtil;
this.telegramDetailService = telegramDetailService;
}
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String authHeader = request.getHeader("Authorization");
if (authHeader != null && !authHeader.isEmpty() && authHeader.startsWith("Bearer ")) {
String jwt = authHeader.substring(7);
// System.out.println(jwt);
if (jwt.isEmpty()) {
response.sendError(HttpServletResponse.SC_BAD_REQUEST,
"Invalid JWT Token in Bearer Header");
} else {
try {
String username = jwtUtil.validateTokenAndRetrieveClaim(jwt);
// System.out.println(username);
TelegramUserDetails telegramUserDetails = telegramDetailService.loadUserByUsername(username);
// System.out.println("tut");
UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(telegramUserDetails,
telegramUserDetails.getPassword(),
telegramUserDetails.getAuthorities());
if (SecurityContextHolder.getContext().getAuthentication() == null) {
SecurityContextHolder.getContext().setAuthentication(auth);
System.out.println(SecurityContextHolder.getContext());
}
} catch (JWTVerificationException ex) {
System.out.println("ne proshlo");
}
}
}
filterChain.doFilter(request, response);
}
}
JWTUtil:
package ru.alishev.springcourse.FirstSecurityApp.security;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.DecodedJWT;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.time.ZonedDateTime;
#Component
public class JWTUtil {
public String generateToken(String username) {
Date expirationDate = Date.from(ZonedDateTime.now().plusMinutes(60).toInstant());
// 60 минут действиe токена
return JWT.create()
.withSubject("Telegram user details")
.withClaim("username", username)
.withIssuedAt(new Date())
.withIssuer("Quiz game")
.withExpiresAt(expirationDate)
.sign(Algorithm.HMAC256("secret"));
}
public String validateTokenAndRetrieveClaim(String token) throws JWTVerificationException {
JWTVerifier verifier = JWT.require(Algorithm.HMAC256("secret"))
.withSubject("Telegram user details")
.withIssuer("Quiz game")
.build();
DecodedJWT jwt = verifier.verify(token);
return jwt.getClaim("username").asString();
}
}
here is full hardcoding, but according to the idea it should work, but I get a 403 error
please help me fix
full code:
https://github.com/Include5/gameTG
I changed
.antMatchers("/api/tg_users/auth/telegram").hasRole("Include0")
to
.antMatchers("/api/tg_users/auth/telegram").hasAuthority("Include0")
and it worked

Opaque Token Implementation in spring security

im trying to create a secured spring rest api for the security i want to use opaque token stored in the database so that if the client query on the api with a bearer token . the server will check on the database if the token exist if the token is valid and get the user and the privilege and check if the user have the authority to do the request. i've done some research on the net but didn't found result that can be understood by a beginner. how can i implement this.
method 1 found method 2 i have found this two methods but i dont know where too implements the database verification and validation
after a lot of research i've found this and it is working
first i've created a Authorization filter like this :
package com.example.bda_test_11.security;
import com.example.bda_test_11.model.BdaUser;
import com.example.bda_test_11.model.Token;
import com.example.bda_test_11.repository.TokenRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
#Slf4j
public class AuthorizationFilter extends BasicAuthenticationFilter {
private final TokenRepository tokenRepository;
public AuthorizationFilter(AuthenticationManager authenticationManager,TokenRepository tokenRepository) {
super(authenticationManager);
this.tokenRepository = tokenRepository;
}
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String tokenCode = request.getHeader(HttpHeaders.AUTHORIZATION);
log.info(tokenCode);
if(tokenCode == null ) {
filterChain.doFilter(request,response);
return;
}
Token token = tokenRepository.findByCode(tokenCode).orElse(null);
if (token == null) {
filterChain.doFilter(request,response);
return;
}
BdaUser user = token.getUser();
UsernamePasswordAuthenticationToken userToken = new UsernamePasswordAuthenticationToken(user.getLogin(),null,user.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(userToken);
filterChain.doFilter(request,response);
}
}
and a UsernamePasswordAuthenticationFilter like this
package com.example.bda_test_11.security;
import com.example.bda_test_11.security.domain.LoginCredentials;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
#Slf4j
public class JsonObjectAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private final ObjectMapper objectMapper = new ObjectMapper();
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response){
try{
BufferedReader reader = request.getReader();
StringBuilder stringBuilder = new StringBuilder();
String line;
while ((line=reader.readLine())!=null){
stringBuilder.append(line);
}
LoginCredentials authRequest = objectMapper.readValue(stringBuilder.toString(),LoginCredentials.class);
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
authRequest.getLogin(),
authRequest.getPassword()
);
setDetails(request,token);
log.info(token.toString());
return this.getAuthenticationManager().authenticate(token);
} catch (IOException e){
throw new RuntimeException(e);
}
}
}
if the connection is successful we generate a token like :
package com.example.bda_test_11.security;
import com.example.bda_test_11.model.BdaUser;
import com.example.bda_test_11.model.Token;
import com.example.bda_test_11.repository.TokenRepository;
import com.example.bda_test_11.service.BdaUserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
#Slf4j #Component
public class AuthSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
private final TokenRepository tokenRepository;
private final BdaUserService userService;
#Autowired
public AuthSuccessHandler(TokenRepository tokenRepository, BdaUserService userService) {
this.tokenRepository = tokenRepository;
this.userService = userService;
}
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {
UserDetails principal = (UserDetails) authentication.getPrincipal();
BdaUser user = userService.findByLogin(principal.getUsername());
Token token = new Token(user);
tokenRepository.save(token);
log.info(token.getCode());
response.addHeader("Authorization",token.getCode());
response.addHeader("Content-Type","application/json");
response.getWriter().write("{\"token\":"+token.getCode()+",\"login\":"+user.getLogin());
}
}
and then ive configured the filterChain bean like this
package com.example.bda_test_11.security;
import com.example.bda_test_11.repository.TokenRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.HttpStatusEntryPoint;
#Configuration
public class BdaSecurity {
private final AuthenticationManager authenticationManager;
private final AuthSuccessHandler authSuccessHandler;
private final TokenRepository tokenRepository;
#Autowired
public BdaSecurity(AuthenticationManager authenticationManager, AuthSuccessHandler authSuccessHandler, TokenRepository tokenRepository) {
this.authenticationManager = authenticationManager;
this.authSuccessHandler = authSuccessHandler;
this.tokenRepository = tokenRepository;
}
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.cors()
.and()
.csrf()
.disable()
.authorizeHttpRequests((auth)->{
try {
auth
.antMatchers("/api/admin").hasAuthority("ADMIN")
.antMatchers("/api/user").hasAuthority("USER")
.anyRequest().permitAll()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilter(authenticationFilter())
.addFilter(new AuthorizationFilter(authenticationManager,tokenRepository))
.exceptionHandling()
.authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED));
} catch (Exception e) {
throw new RuntimeException(e);
}
})
.httpBasic(Customizer.withDefaults());
return http.build();
}
#Bean
public JsonObjectAuthenticationFilter authenticationFilter() {
JsonObjectAuthenticationFilter filter = new JsonObjectAuthenticationFilter();
filter.setAuthenticationSuccessHandler(authSuccessHandler);
filter.setAuthenticationManager(authenticationManager);
return filter;
}
}

Token is generating as null for login using springboot

I would like to generate the token for login using springboot, The token is generating while checking using postman, but I am getting null in console
Authenticate controller
package com.demo.grocery.controller;
import com.demo.grocery.config.JwtUtils;
import com.demo.grocery.model.JwtRequest;
import com.demo.grocery.model.JwtResponse;
import com.demo.grocery.model.User;
import com.demo.grocery.service.impl.UserDetailsServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.web.bind.annotation.*;
import java.security.Principal;
#RestController
public class AuthenticateController {
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private UserDetailsServiceImpl userDetailsServiceImpl;
#Autowired
private JwtUtils jwtUtils;
//generate token
#CrossOrigin("http://localhost:4200")
#PostMapping("/generate-token")
public ResponseEntity<?> generateToken(#RequestBody JwtRequest jwtRequest) throws Exception
{
try {
this.authenticate(jwtRequest.getUsername(), jwtRequest.getPassword());
} catch (UsernameNotFoundException e) {
// TODO: handle exception
e.printStackTrace();
throw new Exception("User Not Found");
}
////////////authenticate
UserDetails userDetails = this.userDetailsServiceImpl.loadUserByUsername(jwtRequest.getUsername());
String token = this.jwtUtils.generateToken(userDetails);
return ResponseEntity.ok(new JwtResponse(token));
}
private void authenticate(String username, String password) throws Exception
{
try {
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
} catch (DisabledException e) {
e.printStackTrace();
throw new Exception("USER DISABLED "+e.getMessage());
}
catch(BadCredentialsException e)
{
throw new Exception("Invalid Credentials "+e.getMessage());
}
}
//returns the details of current logged in user
#CrossOrigin("http://localhost:4200")
#GetMapping("/current-user")
public User getCurrentUser(Principal principal)
{
return (User) this.userDetailsServiceImpl.loadUserByUsername(principal.getName());
}
}
JwtAuthenticationFilter
package com.demo.grocery.config;
import com.demo.grocery.service.impl.UserDetailsServiceImpl;
import io.jsonwebtoken.ExpiredJwtException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
#Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
#Autowired
private UserDetailsServiceImpl userDetailsService;
#Autowired
private JwtUtils jwtUtil;
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
final String requestTokenHeader = request.getHeader("Authorization");
System.out.println(requestTokenHeader);
String username = null;
String jwtToken = null;
if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer ")) {
// yes
jwtToken = requestTokenHeader.substring(7);
try {
username = this.jwtUtil.extractUsername(jwtToken);
} catch (ExpiredJwtException e) {
e.printStackTrace();
System.out.println("jwt token has expired..");
}
catch (Exception e) {
e.printStackTrace();
System.out.println("error");
}
}
else {
System.out.println("Invalid Token, Not Starts with bearer String");
}
//validate Token
if(username!=null && SecurityContextHolder.getContext().getAuthentication() == null)
{
final UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
if(this.jwtUtil.validateToken(jwtToken, userDetails))
{
//token is valid
UsernamePasswordAuthenticationToken usernamePasswordAuthentication = new UsernamePasswordAuthenticationToken(userDetails,null,userDetails.getAuthorities());
usernamePasswordAuthentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthentication);
}
else
{
System.out.println("Token is not Valid");
}
}
filterChain.doFilter(request, response);
}
}
Service
package com.demo.grocery.service.impl;
import com.demo.grocery.model.User;
import com.demo.grocery.repository.UserRepo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
#Service
public class UserDetailsServiceImpl implements UserDetailsService {
#Autowired
private UserRepo userRepository;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = this.userRepository.findByUsername(username);
if (user == null) {
System.out.println("User Not Found");
throw new UsernameNotFoundException("No User Found!!");
}
return user;
}
}
Output in intelliji
Postman output
Look into the screenshots and help me to solve the issue. Why it is storing as null?
For your method to work you should pass your JWT in Postman using Authorization -> Type: Bearer Token.
As you can see in your console log, there's a message "Invalid Token, Not Starts with bearer String", which is printed from your JwtAuthenticationFilter when there's no Authorization header or it doesn't start with "Bearer".
At this point your user is not authenticated, so there's no Principal object in SecurityContextHolder when controller method is executed.
For these cases you should convert response to 401 UNAUTHORIZED status. For example, you can do it in your filter by writing straight to response.
But implementing your own security mechanism is considered a bad practice. I advise to use spring-security build-in JWT-support, which will automatically respond with 401 when there's no valid token found in Authorization header and will store additional info about missing or invalid token at response WWW-Authenticate header.
Take a look at spring-security official sample of jwt-secured resource-server.

Spring Boot - readMessageInternal of AbstractHttpMessageConverter implementation not getting invoked

Trying to write very simple http message converter to add encryption around request & respose.
Implemented rest controller and messageConverter. writeInternal does get invoked during response. However readInternal does not get invoked when request is sent to the rest end point.
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import org.json.JSONException;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.AbstractHttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.stereotype.Component;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.techm.bm.util.EncryptDecryptUtil;
#Component
public class MessageEncryptionConverter extends AbstractHttpMessageConverter<Object> {
#Autowired
private ObjectMapper objectMapper;
public MessageEncryptionConverter() {
super(MediaType.ALL);
}
#Override
protected boolean supports(Class<?> clazz) {
return true;
}
#Override
protected Object readInternal(Class<? extends Object> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException {
return objectMapper.readValue(decrypt(inputMessage.getBody()), clazz);
}
#Override
protected void writeInternal(Object o, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
outputMessage.getBody().write(encrypt(objectMapper.writeValueAsBytes(o)));
}
The below code is for encryption and decryption of request and response body For overall Project to modify response body and request parameters. Please check this code is works for me
For Request Encryption refer readInternal
below is the front-end or postman request sending, you have to add encrypted request in Json formate
Ex.{data : "+++++++++++++YOUR_ENCRYPTED_STRING++++++++++"}
For Response Descryption refer writeInternal
sending back to your front-end or postman request response with encrypted string
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import org.json.JSONException;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.AbstractHttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.stereotype.Component;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.techm.bm.util.EncryptDecryptUtil;
#Component
public class MessageEncryptionConverter extends AbstractHttpMessageConverter<Object> {
#Autowired
private ObjectMapper objectMapper;
public MessageEncryptionConverter() {
super(MediaType.ALL);
}
#Override
protected boolean supports(Class<?> clazz) {
return true;
}
#Override
protected Object readInternal(Class<? extends Object> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException {
return objectMapper.readValue(decrypt(inputMessage.getBody()), clazz);
}
#Override
protected void writeInternal(Object o, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
outputMessage.getBody().write(encrypt(objectMapper.writeValueAsBytes(o)));
}
// Convert request to input stream for sending to the controller
private InputStream decrypt(InputStream inputStream) throws IOException {
//this is API request params
Writer writer = new StringWriter();
char[] buffer = new char[1024];
try {
Reader reader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
int n;
while ((n = reader.read(buffer)) != -1) {
writer.write(buffer, 0, n);
}
} catch (UnsupportedEncodingException e) {
logger.error("UnsupportedEncodingException" + e.getMessage());
} catch (IOException e) {
logger.error("IOException" + e.getMessage());
} finally {
inputStream.close();
}
try {
JSONObject requestJsonObject = new JSONObject(writer.toString().replace("\n", ""));
// Add your decryption method the object data will get your encrypted string data and return with string of your JSON object
String decryptRequestString = EncryptionUtils.decryptText(requestJsonObject.getString("data"));
if (decryptRequestString != null) {
return new ByteArrayInputStream(decryptRequestString.getBytes(StandardCharsets.UTF_8));
} else {
return inputStream;
}
} catch (JSONException err) {
logger.error("Error" + err.toString());
} catch (NoSuchAlgorithmException | BadPaddingException | DigestException | InvalidKeyException
| InvalidAlgorithmParameterException | NoSuchPaddingException | IllegalBlockSizeException e) {
logger.info("NoSuchAlgorithmException | BadPaddingException | DigestException | InvalidKeyException | " +
"InvalidAlgorithmParameterException | NoSuchPaddingException | IllegalBlockSizeException:: " + e.getMessage());
}
return inputStream;
}
private byte[] encrypt(Object obj) throws JsonProcessingException {
// do your encryption here
try {
String stringFromObject = objectMapper.writeValueAsString(obj);
// Add your encryption method
String encrypt = EncryptionUtils.encryptText(stringFromObject);
return objectMapper.writeValueAsBytes(encrypt);
} catch (Exception e) {
logger.info("Error While Encrypt data :: " + e.getMessage());
return objectMapper.writeValueAsBytes(obj);
}
}
}
Reference code Spring Boot - Encrypt JSON data
Hope this code is working for you
It is invoking if the request has body and "supports" method return ture

Add custom claim in authentication filter. Get user id in filter. Spring boot

I want to add user's id as custom claim into my token.
But i cant get users id in filter because dependency injection isnt working in filters. I tried using constructor of my UserService but in this service i have repository which im #Autowiring so in debug mode i have seen that userRepository field is null.
My question is how i will add this custom claim?
Maybe is it another way to add this.
I was following this tutorial (without "Aside" chapter)
https://auth0.com/blog/implementing-jwt-authentication-on-spring-boot/#User-Authentication-and-Authorization-on-Spring-Boot
this is my filter where im trying to add this claim
package com.kamczi.auth;
import com.auth0.jwt.JWT;
import static com.auth0.jwt.algorithms.Algorithm.HMAC512;
import com.fasterxml.jackson.databind.ObjectMapper;
import static com.kamczi.auth.SecurityConstants.EXPIRATION_TIME;
import static com.kamczi.auth.SecurityConstants.HEADER_STRING;
import static com.kamczi.auth.SecurityConstants.SECRET;
import static com.kamczi.auth.SecurityConstants.TOKEN_PREFIX;
import com.kamczi.entities.User;
import com.kamczi.repository.UserRepository;
import com.kamczi.services.UserService;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
/**
*
* #author Kamil
*/
public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private AuthenticationManager authenticationManager;
public JWTAuthenticationFilter(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
try{
User user = new ObjectMapper()
.readValue(request.getInputStream(), User.class);
return authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
user.getUsername(),
user.getPassword(),
new ArrayList<>())
);
} catch(IOException e){
throw new RuntimeException(e);
}
}
#Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
String username = ((org.springframework.security.core.userdetails.User) authResult.getPrincipal()).getUsername();
String token = JWT.create()
//.withClaim("id", userService.findByUsername(username).getUser_id()) need implementation
.withSubject(username)
.withExpiresAt(new Date(System.currentTimeMillis()+EXPIRATION_TIME))
.sign(HMAC512(SECRET.getBytes()));
response.addHeader(HEADER_STRING, TOKEN_PREFIX + token);
}
}
Try to mark your filter with #Component. The #Autowired is working only with Spring-managed beans (components).
Or you can add the filter manually using the constructor and pass the repository to it.
#Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
UsersRepository usersRepo;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterAt(new JWTAuthenticationFilter(usersRepo), UsernamePasswordAuthenticationFilter.class);
}
}

Resources