I was trying to implement basic authentication and authorization in springboot. But i am getting 403 Forbidden error when i send a get request to an endpoint.I have already added JWT token in Authorization header. I am trying to send a Get request to "/user".
Here's the code which I have written
UserController
#RequestMapping(value = "/user")
#RestController
public class UserController {
#Autowired
UserService userService;
#PostMapping
public UserDetailsResponseModel createUser(#RequestBody UserDetailsRequestModel userRequestObject)
{
UserDetailsResponseModel userResponse = new UserDetailsResponseModel();
UserDTO userDto = new UserDTO();
BeanUtils.copyProperties(userRequestObject,userDto);
UserDTO createdUser = userService.createUser(userDto);
BeanUtils.copyProperties(createdUser,userResponse);
return userResponse;
}
#GetMapping
public String getUser()
{
return "Get user was called";
}
}
WebSecurity Class
#Configuration
#EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter {
private final UserService userService;
private final BCryptPasswordEncoder bCryptPasswordEncoder;
public WebSecurity(UserService userService,BCryptPasswordEncoder bCryptPasswordEncoder)
{
this.userService = userService;
this.bCryptPasswordEncoder = bCryptPasswordEncoder;
}
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests()
.antMatchers(HttpMethod.POST, SecurityConstants.SIGN_UP_URL).permitAll()
.anyRequest().authenticated()
.and()
.addFilter(new AuthenticationFilter(authenticationManager()))
.addFilter(new AuthorizationFilter(authenticationManager()));
}
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService).passwordEncoder(bCryptPasswordEncoder);
}
}
Authorization Filter
public class AuthorizationFilter extends BasicAuthenticationFilter {
public AuthorizationFilter(AuthenticationManager authenticationManager)
{
super(authenticationManager);
}
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
String header = request.getHeader(SecurityConstants.HEADER_STRING);
if(header == null || !header.startsWith(SecurityConstants.TOKEN_PREFIX));
{
chain.doFilter(request,response);
}
UsernamePasswordAuthenticationToken authenticationToken = getAuthentication(request);
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
chain.doFilter(request,response);
}
private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
String token = request.getHeader(SecurityConstants.HEADER_STRING);
token = token.replace(SecurityConstants.TOKEN_PREFIX,"");
String user = Jwts.parser()
.setSigningKey(SecurityConstants.TOKEN_SECRET)
.parseClaimsJws(token)
.getBody()
.getSubject();
if(user != null){
return new UsernamePasswordAuthenticationToken(user,null,new ArrayList<>());
}
return null;
}
}
Authentication Filter
public class AuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private final AuthenticationManager authenticationManager;
public AuthenticationFilter(AuthenticationManager authenticationManager)
{
this.authenticationManager = authenticationManager;
}
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
try {
UserLoginRequestModel creds = new ObjectMapper().readValue(request.getInputStream(),UserLoginRequestModel.class);
return authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
creds.getEmail(),
creds.getPassword(),
new ArrayList<>()
)
);
}
catch (IOException ex){
throw new RuntimeException(ex);
}
}
#Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
String userName = ((User) authResult.getPrincipal()).getUsername();
String token = Jwts.builder()
.setSubject(userName)
.setExpiration(new Date(System.currentTimeMillis()+SecurityConstants.EXPIRATION_TIME))
.signWith(SignatureAlgorithm.HS512,SecurityConstants.TOKEN_SECRET)
.compact();
UserService userService = (UserService) SpringApplicationContext.getBean("userServiceImplementation");
UserDTO userDTO = userService.getUser(userName);
response.addHeader(SecurityConstants.HEADER_STRING,SecurityConstants.TOKEN_PREFIX+token);
response.addHeader("UserID",userDTO.getUserId());
}
}
Related
I am working on spring security with JWT. i am getting 403 forbidden error.
The OncePerRequest filter for my jwt token is as
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String authHeader = request.getHeader("Authorization");
if(authHeader!=null && !authHeader.equals("") && authHeader.startsWith("Bearer")){
String jwt = authHeader.substring(7);
if (jwt == null || jwt.isEmpty()){
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid JWT token in bearer header");
} else {
try{
String username = jwtUtil.validateTokenAndRetrieveSubject(jwt);
UserDetails userDetails = myUserDetailsService.loadUserByUsername(username);
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
username,
userDetails.getPassword(),
userDetails.getAuthorities()
);
if (SecurityContextHolder.getContext().getAuthentication() == null){
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
}
System.out.println("this is from JWTFILTER -> " + username);
}catch (Exception e ){
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid JWT token: " +e);
}
}
}
filterChain.doFilter(request,response);
and my JWTUtil for token generation and validation.
#Component
public class JWTUtil {
#Value("${jwt-secret}")
private String SECRET_KEY;
#Value("${jwt-issuer}")
private String issuer;
#Value("${jwt-claim}")
private String UserName;
public String generateToken(String username) throws IllegalArgumentException, JWTCreationException{
return JWT.create()
.withClaim(UserName, username)
.withIssuedAt(new Date(System.currentTimeMillis()))
// .withExpiresAt(new Date(System.currentTimeMillis()/1000 + 3600 ))
.withIssuer(issuer)
.sign(Algorithm.HMAC256(SECRET_KEY));
}
public String validateTokenAndRetrieveSubject(String token) throws JWTVerificationException{
JWTVerifier verifier = JWT.require(Algorithm.HMAC256(SECRET_KEY))
.withIssuer(issuer)
.build();
DecodedJWT jwt = verifier.verify(token);
return jwt.getClaim(UserName).asString();
}
}
and MyUserDetailsService. I don't have password for the user in my microservice so i am sending my username again org.springframework.security.core.userdetails.User().
#Component
public class MyUserDetailsService implements UserDetailsService {
#Autowired private UserRepo userRepo;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Optional<User> userResource = userRepo.findUserByNameEquals(username);
if (!userResource.isPresent())
throw new UsernameNotFoundException("Could not found this username = " + username);
User user = userResource.get();
return new org.springframework.security.core.userdetails.User(
username,
user.getName(),
Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER"))
);
}
}
and at the last the SecurityConfig in which i am using my custom authenticationEntryPoint.
#Component
#EnableWebSecurity
#AllArgsConstructor
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig {
#Autowired private MyUserDetailsService uds;
#Autowired private JWTFilter jwtFilter;
private MyAuthenticationEntryPoint myAuthenticationEntryPoint;
#Bean
protected SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception{
httpSecurity.csrf().disable().httpBasic().disable()
.cors()
.and()
.authorizeHttpRequests()
.antMatchers("/api/user/**").permitAll()
.antMatchers("/api/symbol/**").hasRole("ROLE_USER")
.and().userDetailsService(uds)
.exceptionHandling()
.authenticationEntryPoint(myAuthenticationEntryPoint)
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().formLogin().disable();
httpSecurity.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);
return httpSecurity.build();
}
#Bean
public PasswordEncoder passwordEncoder (){
return new BCryptPasswordEncoder();
}
#Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception{
return authenticationConfiguration.getAuthenticationManager();
}
}
its my first time working on jwt so i have no idea about the issue.
After registering and logging in with postman, after verifying the accessToken, when I write a post, a rejection error occurs. I don't know why.
SecurityConfig
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private CustomUserDetailsService userDetailsService;
#Autowired
private JwtAuthenticationEntryPoint authenticationEntryPoint;
#Bean
public JwtAuthenticationFilter jwtAuthenticationFilter(){
return new JwtAuthenticationFilter();
}
#Bean
PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint)
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers(HttpMethod.GET, "/api/**").permitAll()
.antMatchers("/api/auth/**").permitAll()
.anyRequest()
.authenticated();
http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
JWTAuthResponse
#Getter
#Setter
public class JWTAuthResponse {
private String accessToken;
private String tokenType = "Bearer";
public JWTAuthResponse(String accessToken) {
this.accessToken = accessToken;
}
}
JwtAuthenticationFilter
public class JwtAuthenticationFilter extends OncePerRequestFilter {
#Autowired
private JwtTokenProvider tokenProvider;
#Autowired
private CustomUserDetailsService customUserDetailsService;
#Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
// get JWT (token) from http request
String token = getJWTfromRequest(request);
// validate token
if(StringUtils.hasText(token) && tokenProvider.validateToken(token)){
// get username from token
String username = tokenProvider.getUsernameFromJWT(token);
// load user associated with token
UserDetails userDetails = customUserDetailsService.loadUserByUsername(username);
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities()
);
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
// set spring security
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
}
filterChain.doFilter(request, response);
}
// Bearer <accessToken>
private String getJWTfromRequest(HttpServletRequest request){
String bearerToken = request.getHeader("Authorization");
if(StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")){
return bearerToken.substring(7, bearerToken.length());
}
return null;
}
}
AuthController
#RestController
#RequestMapping("/api/auth")
#RequiredArgsConstructor
public class AuthController {
private final AuthenticationManager authenticationManager;
private final UserRepository userRepository;
private final RoleRepository roleRepository;
private final PasswordEncoder passwordEncoder;
private final JwtTokenProvider tokenProvider;
#PostMapping("/signin")
public ResponseEntity<JWTAuthResponse> authenticateUser(#RequestBody LoginDto loginDto) {
Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(
loginDto.getUsernameOrEmail(), loginDto.getPassword()));
SecurityContextHolder.getContext().setAuthentication(authentication);
// get token from tokenProvider
String token = tokenProvider.generateToken(authentication);
return ResponseEntity.ok(new JWTAuthResponse(token));
}
#PostMapping("/signup")
public ResponseEntity<?> registerUser(#RequestBody SignUpDto signUpDto) {
// add check for username exists in a DB
if (userRepository.existsByUsername(signUpDto.getUsername())) {
return new ResponseEntity<>("Username is already taken!", HttpStatus.BAD_REQUEST);
}
// add check for email exists in a DB
if (userRepository.existsByEmail(signUpDto.getEmail())) {
return new ResponseEntity<>("Email is already taken!", HttpStatus.BAD_REQUEST);
}
// create user object
User user = new User();
user.setName(signUpDto.getName());
user.setUsername(signUpDto.getUsername());
user.setEmail(signUpDto.getEmail());
user.setPassword(passwordEncoder.encode(signUpDto.getPassword()));
Role roles = roleRepository.findByName("ROLE_ADMIN").isPresent() ? roleRepository.findByName("ROLE_ADMIN").get() : null;
userRepository.save(user);
return new ResponseEntity<>("User registered successfully", HttpStatus.OK);
}
PostController
#PreAuthorize("hasRole('ADMIN')")
#PostMapping
public ResponseEntity<PostDto> createPost(#Valid #RequestBody PostDto postDto) {
return new ResponseEntity<>(postService.createPost(postDto), HttpStatus.CREATED);
}
and my error is
Failed to authorize ReflectiveMethodInvocation: public org.springframework.http.ResponseEntity com.demo.blog.controller.PostController.createPost(com.demo.blog.dto.PostDto); target is of class [com.demo.blog.controller.PostController] with attributes [[authorize: 'hasRole('ADMIN')', filter: 'null', filterTarget: 'null']]
Resolved [org.springframework.security.access.AccessDeniedException: Access is denied]
I gave admin privileges when signing up, but I think it is a permission error.
I am new to Spring security, I have implemented a basic user login functionality for my app using JWT. Aside from checking for username and password at login I would like to add other parameters such as a "account is verified" boolean condition but I am not sure where to add this requirement. Additionally, I need to return a 403 forbidden response status message if the "account is verified" condition is false and return a different response status message if the username password combination isn't found at all. Here Is the code I currently have which correctly handles the login of an existing user (without checking for the "account is verified" condition) and always throws a 401 when the user is found. Any feedback would be helpful.
WebSecurityConfigurerAdapter
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private final ApplicationUserDetailsService applicationUserDetailsService;
private final BCryptPasswordEncoder bCryptPasswordEncoder;
public WebSecurityConfig(ApplicationUserDetailsService userDetailsService) {
this.applicationUserDetailsService = userDetailsService;
this.bCryptPasswordEncoder = new BCryptPasswordEncoder();
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.cors()
.and()
.csrf()
.disable()
.authorizeRequests()
.antMatchers("/**")
.permitAll()
.anyRequest()
.authenticated()
.and()
.addFilter(new AuthenticationFilter(authenticationManager()))
.addFilter(new AuthorizationFilter(authenticationManager()))
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
#Bean
public PasswordEncoder encoder() {
return this.bCryptPasswordEncoder;
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(applicationUserDetailsService)
.passwordEncoder(bCryptPasswordEncoder);
}
}
UserDetailsService
public class ApplicationUserDetailsService implements UserDetailsService {
private final ApplicationUserRepository applicationUserRepository;
public ApplicationUserDetailsService(ApplicationUserRepository applicationUserRepository) {
this.applicationUserRepository = applicationUserRepository;
}
#Override
public UserDetails loadUserByUsername(String nickname)
throws UsernameNotFoundException, UserIsNotActiveException {
Optional<ApplicationUser> applicationUser =
applicationUserRepository.findByNickname(nickname);
if (!applicationUser.isPresent()) {
throw new UsernameNotFoundException(nickname);
}
return new User(
applicationUser.get().getNickname(),
applicationUser.get().getPassword(),
emptyList());
}
}
AuthenticationFilter
public class AuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private AuthenticationManager authenticationManager;
public AuthenticationFilter(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
#Override
public Authentication attemptAuthentication(HttpServletRequest req, HttpServletResponse res)
throws AuthenticationException {
try {
ApplicationUser applicationUser =
new ObjectMapper().readValue(req.getInputStream(), ApplicationUser.class);
return authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
applicationUser.getNickname(),
applicationUser.getPassword(),
new ArrayList<>()));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
#Override
protected void successfulAuthentication(
HttpServletRequest req,
HttpServletResponse res,
FilterChain chain,
Authentication auth) {
Date exp = new Date(System.currentTimeMillis() + EXPIRATION_TIME);
Key key = Keys.hmacShaKeyFor(KEY.getBytes());
Claims claims = Jwts.claims().setSubject(((User) auth.getPrincipal()).getUsername());
String token =
Jwts.builder()
.setClaims(claims)
.signWith(key, SignatureAlgorithm.HS512)
.setExpiration(exp)
.compact();
res.addHeader("token", token);
}
}
AuthorizationFilter
public AuthorizationFilter(AuthenticationManager authManager) {
super(authManager);
}
#Override
protected void doFilterInternal(
HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws IOException, ServletException {
String header = request.getHeader(HEADER_NAME);
if (header == null) {
chain.doFilter(request, response);
return;
}
UsernamePasswordAuthenticationToken authentication = authenticate(request);
SecurityContextHolder.getContext().setAuthentication(authentication);
chain.doFilter(request, response);
}
private UsernamePasswordAuthenticationToken authenticate(HttpServletRequest request) {
String token = request.getHeader(HEADER_NAME);
if (token != null) {
Jws<Claims> user =
Jwts.parserBuilder()
.setSigningKey(Keys.hmacShaKeyFor(KEY.getBytes()))
.build()
.parseClaimsJws(token);
if (user != null) {
return new UsernamePasswordAuthenticationToken(user, null, new ArrayList<>());
} else {
return null;
}
}
return null;
}
ApplicationUser
public class ApplicationUser {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private UUID id;
#Column(unique = true)
String email;
#Column(unique = true)
String nickname;
String biography;
String password; // Hashed
#Builder.Default boolean isActive = false;
}
The interface UserDetails (that is returned by the UserDetailsService) has some utility methods that can help you with it.
While the account is not activated, you can return false from the UserDetails#isEnabled method, or maybe you can use UserDetails#isAccountNonLocked as well.
Those methods will then be automatically validated on the AbstractUserDetailsAuthenticationProvider$Default(Pre/Post)AuthenticationChecks class.
After the user goes through the activation flow, you can change the property to true and it will allow the user to authenticate.
Tip: add the logging.level.org.springframework.security=TRACE to your application.properties to help to debug.
First time posting, please excuse any mistakes in the question.
I'm building a simple website with spring boot and angular and implemented a jwt login using some help from the internet. I can't seem to make it work because the backend is not receiving the username and password credentials when logging in. Below is part of my code:
The spring boot security configuration class. When configure method is triggered, it prints out that the username and password sent are null.
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
#Autowired
private UserDetailsService userDetailsService;
#Autowired
private JwtRequestFilter jwtRequestFilter;
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.csrf().disable()
.authorizeRequests()
.antMatchers("/api/posts", "/api/forums").permitAll()
.antMatchers("/api/auth/admin/**").hasRole("ADMIN")
.antMatchers("/api/auth/**").hasAnyRole("ADMIN", "USER")
.and()
.exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint)
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.formLogin()
.loginPage("/api/auth/login")
.failureHandler(new AuthenticationFailureHandler(){
#Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException, ServletException {
String email = request.getParameter("email");
String error = exception.getMessage();
System.out.println("A failed login attempt with email: " + email + " and password: " + request.getParameter("password") + ". Reason: " + error);
}
})
.permitAll()
.and()
.httpBasic();
httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
}
#Autowired
protected void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
#Bean
protected PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean(BeanIds.AUTHENTICATION_MANAGER)
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
Jwt support classes
#Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint, Serializable {
private static final long serialVersionUID = -7858869558953243875L;
#Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
}
}
#Component
public class JwtRequestFilter extends OncePerRequestFilter {
#Autowired
private UserDetailsService userDetailsService;
#Autowired
private JwtTokenUtil jwtTokenUtil;
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
final String requestTokenHeader = request.getHeader("Authorization");
String username = null;
String jwtToken = null;
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.userDetailsService.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);
}
}
#Component
public class JwtTokenUtil implements Serializable {
private static final long serialVersionUID = -2550185165626007488L;
public static final long JWT_TOKEN_VALIDITY = 2*60*60;
#Value("${jwt.secret}")
private byte[] secret;
public String getUsernameFromToken(String token) {
return getClaimFromToken(token, Claims::getSubject);
}
public Date getIssuedAtDateFromToken(String token) {
return getClaimFromToken(token, Claims::getIssuedAt);
}
public Date getExpirationDateFromToken(String token) {
return getClaimFromToken(token, Claims::getExpiration);
}
public <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver) {
final Claims claims = getAllClaimsFromToken(token);
return claimsResolver.apply(claims);
}
private Claims getAllClaimsFromToken(String token) {
return Jwts.parserBuilder().setSigningKey(Keys.hmacShaKeyFor(secret)).build().parseClaimsJws(token).getBody();
}
private Boolean isTokenExpired(String token) {
final Date expiration = getExpirationDateFromToken(token);
return expiration.before(new Date());
}
private Boolean ignoreTokenExpiration(String token) {
return false;
}
public String generateToken(UserDetails userDetails) {
Map<String, Object> claims = new HashMap<>();
return doGenerateToken(claims, userDetails.getUsername());
}
private String doGenerateToken(Map<String, Object> claims, String subject) {
return Jwts.builder().setClaims(claims).setSubject(subject).setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + JWT_TOKEN_VALIDITY*1000)).signWith(Keys.hmacShaKeyFor(secret), SignatureAlgorithm.HS512).compact();
}
public Boolean canTokenBeRefreshed(String token) {
return (!isTokenExpired(token) || ignoreTokenExpiration(token));
}
public Boolean validateToken(String token, UserDetails userDetails) {
final String username = getUsernameFromToken(token);
return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
}
}
These snippets are part of the auth controller and service classes which are triggered when a post request with login credentials is made. Debugging mysql here also shows that the request username and password are null. LoginRequest and LoginResponse used here just have username, password and username, jwtToken fields respectively.
#Controller
#RequestMapping("/api/auth")
public class AuthController {
private final AuthService authService;
#Autowired
public AuthController(AuthService authService) {
this.authService = authService;
}
#PostMapping("/login")
public ResponseEntity<LoginResponse> login(#RequestBody LoginRequest request) throws Exception {
LoginResponse jwtResponse = authService.login(request);
return new ResponseEntity<>(jwtResponse, HttpStatus.OK);
}
}
public LoginResponse login(LoginRequest request) throws Exception {
authenticate(request.getUsername(), request.getPassword());
final UserDetails userDetails = userDetailsService.loadUserByUsername(request.getUsername());
final String token = jwtTokenUtil.generateToken(userDetails);
return new LoginResponse(userDetails.getUsername(), token);
}
private void authenticate(String username, String password) throws Exception {
Objects.requireNonNull(username);
Objects.requireNonNull(password);
try {
Authentication authenticate = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
SecurityContextHolder.getContext().setAuthentication(authenticate);
} catch (BadCredentialsException e) {
throw new Exception("INVALID_CREDENTIALS", e);
}
}
Why are the credentials passed null? This is a backend issue because I checked with postman with no association with frontend code. I tried to send the body with different types like json, xxx-form encoded etc, but nothing seems to work.
Here an image of the postman request.
Is there something wrong with my code? How can I fix this?
I have made a Rest API project with Spring Boot 2. I have used jwt for authentication. I can generate tokens fine. But when I send the generated token in a header with a request, it always redirects me to "/" path instead of the requested path (in my case "/rest/hello").
This is my custom AuthenticationProvider
#Component
public class JwtAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
#Autowired
private JwtValidator jwtValidator;
#Override
protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
}
#Override
protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
JwtAuthenticationToken jwtAuthenticationToken = (JwtAuthenticationToken)authentication;
String token = jwtAuthenticationToken.getToken();
JwtUser jwtUser = jwtValidator.validate(token);
if (jwtUser == null) {
throw new RuntimeException("JWT Token not correct");
}
List<GrantedAuthority> grantedAuthorities = AuthorityUtils.commaSeparatedStringToAuthorityList(jwtUser.getRole());
return new JwtUserDetails(jwtUser.getUserName(), jwtUser.getId(), grantedAuthorities, token);
}
#Override
public boolean supports(Class<?> authentication) {
return JwtAuthenticationToken.class.isAssignableFrom(authentication);
}
}
This is my custom Filter
public class JwtAuthenticationTokenFilter extends AbstractAuthenticationProcessingFilter {
public JwtAuthenticationTokenFilter() {
super("/rest/**");
}
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
String header = request.getHeader("Authorization");
if (header == null || !header.startsWith("Token ")) {
throw new RuntimeException("JWT Token is missing");
}
String authenticationToken = header.substring(6);
JwtAuthenticationToken token = new JwtAuthenticationToken(authenticationToken);
return this.getAuthenticationManager().authenticate(token);
}
public void setAuthenticationManager(AuthenticationManager authenticationManager) {
super.setAuthenticationManager(authenticationManager);
}
public void setAuthenticationSuccessHandler(JwtSuccessHandler jwtSuccessHandler) {
}
#Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
super.successfulAuthentication(request, response, chain, authResult);
chain.doFilter(request, response);
}
}
This is my Security config
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class JwtSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private JwtAuthenticationProvider authenticationProvider;
#Autowired
private JwtAuthenticationEntryPoint entryPoint;
#Bean
public AuthenticationManager authenticationManager() {
return new ProviderManager(Collections.singletonList(authenticationProvider));
}
#Bean
public JwtAuthenticationTokenFilter authenticationTokenFilter(){
JwtAuthenticationTokenFilter filter = new JwtAuthenticationTokenFilter();
filter.setAuthenticationManager(authenticationManager());
filter.setAuthenticationSuccessHandler(new JwtSuccessHandler());
return filter;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests().antMatchers("/rest/**").authenticated().and().exceptionHandling().authenticationEntryPoint(entryPoint)
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.addFilterBefore(authenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class);
http.headers().cacheControl();
}
}
I have custom classes for user details and token as well.