I'm trying to implement JWT authentication in Spring Security. But I don't understand how can I use it without UserDetails from DB, because I get users from remote LDAP server and don't store them in my DB.
How I can replace this fetching UserDetails for another one thing? Any ideas?
Here my JwtAuthenticationFilter class:
package com.kddb_webimporter.security;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
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.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.util.StringUtils;
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 com.kddb_webimporter.models.User;
import java.io.IOException;
public class JwtAuthenticationFilter extends BasicAuthenticationFilter {
// #Autowired
private JwtTokenProvider tokenProvider;
// #Autowired
private CustomUserDetailsService customUserDetailsService;
private static final Logger logger = LoggerFactory.getLogger(JwtAuthenticationFilter.class);
public JwtAuthenticationFilter(
AuthenticationManager authenticationManager,
JwtTokenProvider tokenProvider,
CustomUserDetailsService customUserDetailsService ) {
super(authenticationManager);
this.tokenProvider = tokenProvider;
this.customUserDetailsService = customUserDetailsService;
}
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
try {
String jwt = getJwtFromRequest(request);
if (StringUtils.hasText(jwt) && tokenProvider.validateToken(jwt)) {
UserDetails userDetails = customUserDetailsService.loadUserByUsername("");
/*How to replace it?*/
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
userDetails,
null,
userDetails.getAuthorities()
);
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
} catch (Exception ex) {
logger.error("Could not set user authentication in security context", ex);
}
filterChain.doFilter(request, response);
}
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;
}
}
Related
I followed a tutorial on Spring Security. I have set up an authentication that works: here is my authentication filter.
package com.securitycrud.crudTest.filter;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
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.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
import static org.springframework.util.MimeTypeUtils.APPLICATION_JSON_VALUE;
#Slf4j
public class CustomAuthentificationFilter extends UsernamePasswordAuthenticationFilter {
private final AuthenticationManager authenticationManager;
public CustomAuthentificationFilter(AuthenticationManager authenticationManager){
this.authenticationManager = authenticationManager;
}
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
String username = request.getParameter("username");
String password = request.getParameter("password");
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password);
return authenticationManager.authenticate(authenticationToken);
}
#Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authentication) throws IOException, ServletException {
User user = (User)authentication.getPrincipal();
Algorithm algorithm = Algorithm.HMAC256("secret".getBytes());
String access_token = JWT.create()
.withSubject(user.getUsername())
.withExpiresAt(new Date(System.currentTimeMillis()+ 10 * 60 * 1000))
.withIssuer(request.getRequestURL().toString())
.withClaim("roles", user.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.joining()))
.sign(algorithm);
String refresh_token = JWT.create()
.withSubject(user.getUsername())
.withExpiresAt(new Date(System.currentTimeMillis()+ 30 * 60 * 1000))
.withIssuer(request.getRequestURL().toString())
.withClaim("roles", user.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.joining()))
.sign(algorithm);
Map<String, String> tokens = new HashMap<>();
tokens.put("access_token", access_token);
tokens.put("refresh_token", refresh_token);
response.setContentType(APPLICATION_JSON_VALUE);
new ObjectMapper().writeValue(response.getOutputStream(), tokens);
}
}
Then here is the authorization and config class.
package com.securitycrud.crudTest.security;
import com.securitycrud.crudTest.filter.CustomAuthentificationFilter;
import com.securitycrud.crudTest.filter.CustomAuthorizationFilter;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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.WebSecurityConfiguration;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import static org.springframework.http.HttpMethod.GET;
import static org.springframework.http.HttpMethod.POST;
import static org.springframework.security.config.Customizer.withDefaults;
import static org.springframework.security.config.http.SessionCreationPolicy.STATELESS;
#Configuration #EnableWebSecurity #RequiredArgsConstructor
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final UserDetailsService userDetailsService;
private final BCryptPasswordEncoder bCryptPasswordEncoder;
#Override
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder);
}
#Override
protected void configure(final HttpSecurity http) throws Exception {
CustomAuthentificationFilter customAuthentificationFilter = new CustomAuthentificationFilter(authenticationManagerBean());
customAuthentificationFilter.setFilterProcessesUrl("/api/login");
http.csrf().disable();
http.sessionManagement().sessionCreationPolicy(STATELESS);
// en dessous: les url qu'on ne veut pas sécuriser par l'authentification: ex page d'acceuil
http.authorizeRequests().antMatchers("/api/login", "/api/token/refresh").permitAll();
http.authorizeRequests().antMatchers("/api/users").hasAnyRole("ROLE_ADMIN","ROLE_USER");
http.authorizeRequests().antMatchers(POST, "/api/user/save/**").hasAnyAuthority("ROLE_ADMIN");
http.authorizeRequests().anyRequest().authenticated();
http.addFilter(customAuthentificationFilter);
http.addFilterBefore(new CustomAuthorizationFilter(), UsernamePasswordAuthenticationFilter.class);
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception{
return super.authenticationManagerBean();
}
}
package com.securitycrud.crudTest.filter;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
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;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import static java.util.Arrays.stream;
import static org.springframework.http.HttpHeaders.AUTHORIZATION;
import static org.springframework.http.HttpStatus.FORBIDDEN;
import static org.springframework.util.MimeTypeUtils.APPLICATION_JSON_VALUE;
#Slf4j
public class CustomAuthorizationFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
if(request.getServletPath().equals("/api/login")||request.getServletPath().equals("/api/token/refresh") ){
filterChain.doFilter(request,response);
} else {
String authorizationHeader = request.getHeader(AUTHORIZATION);
if(authorizationHeader != null && authorizationHeader.startsWith("Bearer ")){
try {
String token = authorizationHeader.substring("Bearer ".length());
Algorithm algorithm = Algorithm.HMAC256("secret".getBytes());
JWTVerifier jwtVerifier = JWT.require(algorithm).build();
DecodedJWT decodedJWT = jwtVerifier.verify(token);
String username = decodedJWT.getSubject();
String[] roles = decodedJWT.getClaim("roles").asArray(String.class);
Collection<SimpleGrantedAuthority> authorities = new ArrayList<>();
stream(roles).forEach(role -> {
authorities.add(new SimpleGrantedAuthority(role));
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, null, authorities);
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
try {
filterChain.doFilter(request, response);
} catch (IOException | ServletException e) {
throw new RuntimeException(e);
}
});
}catch (Exception exception){
log.error("error loggin in: {}", exception.getMessage());
response.setHeader("error", exception.getMessage());
response.setStatus(FORBIDDEN.value());
Map<String, String> error = new HashMap<>();
error.put("error_token", exception.getMessage());
response.setContentType(APPLICATION_JSON_VALUE);
new ObjectMapper().writeValue(response.getOutputStream(), error);
}
} else {
filterChain.doFilter(request, response);
}
}
}
}
Nevertheless, when I try on Postman, I connect well, and I get the token well (and I am role user)
Then, I test the GET api/users request.
And there I can't. I get the following error:
403 Forbidden: {
"error_token": "The input is not a valid base 64 encoded string."
}
I don't understand.... I tried to search but no way...
Does anyone have an idea?
When I run the SpringBoot REST API it works fine, authentication is implemented properly and everything is successful, however, when I try to run any of my unit tests I am getting the following error:
APPLICATION FAILED TO START
Description:
Parameter 0 of constructor in com.out.e.config.ApplicationSecurityConfig required a bean of type 'com.out.e.dao.auth.UserRepository' that could not be found.
Action:
Consider defining a bean of type 'com.out.e.dao.auth.UserRepository' in your configuration.
I have no idea what to do as it seems nonsensical to me that it would function correctly when running properly but not when being tested?
Security Config:
import com.out.e.dao.auth.UserRepository;
import com.out.e.filter.JwtTokenFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class ApplicationSecurityConfig extends WebSecurityConfigurerAdapter {
private final UserRepository userRepository;
private final JwtTokenFilter jwtTokenFilter;
public ApplicationSecurityConfig(final UserRepository userRepository, final JwtTokenFilter jwtTokenFilter) {
this.userRepository = userRepository;
this.jwtTokenFilter = jwtTokenFilter;
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(username -> userRepository.findByEmail(username)
.orElseThrow(() -> new UsernameNotFoundException("User not found with email: " + username))
);
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(final HttpSecurity httpSecurity) throws Exception {
httpSecurity.csrf().disable();
httpSecurity.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
httpSecurity.exceptionHandling().authenticationEntryPoint(
(request, response, authException) -> {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED,
authException.getMessage());
}
);
httpSecurity.authorizeRequests()
.antMatchers("/api/v1/auth/**").permitAll()
.anyRequest().authenticated();
httpSecurity.addFilterBefore(jwtTokenFilter, UsernamePasswordAuthenticationFilter.class);
httpSecurity.cors();
}
}
Token filter:
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.util.ObjectUtils;
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 JwtTokenFilter extends OncePerRequestFilter {
private final JwtTokenUtil jwtTokenUtil;
public JwtTokenFilter(final JwtTokenUtil jwtTokenUtil) {
this.jwtTokenUtil = jwtTokenUtil;
}
#Override
protected void doFilterInternal(final HttpServletRequest request, final HttpServletResponse response, final FilterChain filterChain) throws ServletException, IOException {
if (!hasAuthorisationHeader(request)) {
filterChain.doFilter(request, response);
return;
}
String accessToken = getAccessToken(request);
if (!jwtTokenUtil.verifyAccessToken(accessToken)) {
filterChain.doFilter(request, response);
return;
}
setAuthenticationContext(accessToken, request);
filterChain.doFilter(request, response);
}
private void setAuthenticationContext(String accessToken, HttpServletRequest request) {
UserDetails userDetails = getUserDetails(accessToken);
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, null);
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
private UserDetails getUserDetails(String accessToken) {
User userDetails = new User();
//Subject was split into userId and email by a comma so split the comma.
String[] subjects = jwtTokenUtil.getSubject(accessToken).split(",");
userDetails.setUserID(Long.valueOf(subjects[0]));
userDetails.setEmail(subjects[1]);
return userDetails;
}
private boolean hasAuthorisationHeader(HttpServletRequest httpServletRequest) {
String authorizationHeader = httpServletRequest.getHeader("Authorization");
if (ObjectUtils.isEmpty(authorizationHeader) || !authorizationHeader.startsWith("Bearer")) {
return false;
}
return true;
}
private String getAccessToken(HttpServletRequest httpServletRequest) {
String authorizationHeader = httpServletRequest.getHeader("Authorization");
String accessToken = authorizationHeader.split(" ")[1].trim();
System.out.println("Access token: " + accessToken);
return accessToken;
}
}
JWT token util:
import com.out.e.model.auth.User;
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.SignatureException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.Date;
#Component
public class JwtTokenUtil {
private static final Logger LOGGER = LoggerFactory.getLogger(JwtTokenUtil.class);
//24Hours
private static final long EXPIRATION_DURATION = 24 * 60 * 60 * 1000;
#Value("${application.jwt.secretKey}")
private String secretKey;
public String generateAccessToken(User user) {
return Jwts.builder()
.setSubject(user.getUserID() + "," + user.getEmail())
.setIssuer("e")
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_DURATION))
.signWith(SignatureAlgorithm.HS512, secretKey)
.compact();
}
public boolean verifyAccessToken(String accessToken) {
try {
Jwts.parser().setSigningKey(secretKey).parseClaimsJws(accessToken);
return true;
} catch (ExpiredJwtException e) {
LOGGER.error("JWT Access token is expired.", e);
} catch (IllegalArgumentException e) {
LOGGER.error("JWT Access token is null or empty.", e);
} catch (UnsupportedJwtException e) {
LOGGER.error("JWT Access token is not supported.", e);
} catch (SignatureException e) {
LOGGER.error("Failure occurred while during signature process.", e);
}
return false;
}
public String getSubject(String accessToken) {
return parseClaims(accessToken).getSubject();
}
private Claims parseClaims(String accessToken) {
return Jwts.parserBuilder()
.setSigningKey(secretKey)
.build()
.parseClaimsJws(accessToken)
.getBody();
}
}
Repositroy:
import com.out.e.model.auth.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import java.util.List;
import java.util.Optional;
public interface UserRepository extends JpaRepository<User, Long> {
/**
* Finds a user by email.
* #param email email.
* #return the user.
*/
Optional<User> findByEmail(final String email);
}
An example test class just in case:
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.doReturn;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
#AutoConfigureMockMvc
#WebMvcTest(controllers = AuthController.class)
public class AuthControllerTests {
#Autowired
private MockMvc mockMvc;
#Autowired
private ObjectMapper objectMapper;
#MockBean
private AuthService authService;
#MockBean
private UserService userService;
#Test
public void givenAUserDoesNotExistSignUpSuccessfully() throws Exception {
SignUpRequest signUpRequest = new SignUpRequest("tim#outlook.com", "Password-1", "Tim", "Nook", "London", 3000);
MsgResponse msgResponse = new MsgResponse("A new user has been successfully registered.");
doReturn(msgResponse).when(authService).signUpUser(signUpRequest);
String json = objectMapper.writeValueAsString(signUpRequest);
MvcResult mvcResult = mockMvc.perform(post("/api/v1/auth/login")
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.content(json))
.andExpect(status().isOk())
.andReturn();
String response = mvcResult.getResponse().getContentAsString();
assertEquals("A new user has been successfully registered.", response);
}
}
Any help would be greatly appreciated, thanks!
Edit: Add error full:
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'applicationSecurityConfig' defined in file [H:\classes\java\main\com\out\e\config\ApplicationSecurityConfig.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.out.e.dao.auth.UserRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
' Hello,
I have 2 problems with this code now.
In auth Controller, the JWT token generator is empty just showing this {}. It should show
{"username ": "string " , "password" : "string" }
Once is registered through auth controller and checked in Database in Role it is generating a row and id =0 and name= null but it suppose to assign either Normal/Admin right. Also when I try to register 2nd time it gets an error as Internal server error, please help me with this. '
package com.mohammedkhadeer.blog.controllers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
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.UserDetailsService;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.mohammedkhadeer.blog.exceptions.ApiException;
import com.mohammedkhadeer.blog.payloads.JwtAuthRequest;
import com.mohammedkhadeer.blog.payloads.JwtAuthResponse;
import com.mohammedkhadeer.blog.payloads.UserDto;
import com.mohammedkhadeer.blog.security.JwtTokenHelper;
import com.mohammedkhadeer.blog.services.UserService;
#RestController
#RequestMapping("/api/v1/auth/")
public class AuthController {
#Autowired
private JwtTokenHelper jwtTokenHelper;
#Autowired
private UserDetailsService userDetailsService;
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private UserService userService;
#PostMapping("/login")
public ResponseEntity<JwtAuthResponse> createToken(#RequestBody JwtAuthRequest request) throws Exception {
this.authenticate(request.getUsername(), request.getPassword());
UserDetails userDetails = this.userDetailsService.loadUserByUsername(request.getUsername());
String token = this.jwtTokenHelper.generateToken(userDetails);
JwtAuthResponse response = new JwtAuthResponse();
response.setToken(token);
return new ResponseEntity<JwtAuthResponse>(response, HttpStatus.OK);
}
private void authenticate(String username, String password) throws Exception {
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username,
password);
try {
this.authenticationManager.authenticate(authenticationToken);
} catch (BadCredentialsException e) {
System.out.println("Invalid Detials !!");
throw new ApiException("Invalid username or password !!");
}
}
// register new user api
#PostMapping("/register")
public ResponseEntity<UserDto> registerUser(#RequestBody UserDto userDto) {
UserDto registeredUser = this.userService.registerNewUser(userDto);
return new ResponseEntity<UserDto>(registeredUser, HttpStatus.CREATED);
}
}
package com.mohammedkhadeer.blog.security;
import java.io.IOException;
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.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.MalformedJwtException;
#Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
#Autowired
private UserDetailsService userDetailsService;
#Autowired
private JwtTokenHelper jwtTokenHelper;
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
// 1. get token
String requestToken = request.getHeader("Authorization");
// Bearer 2352523sdgsg
System.out.println(requestToken);
String username = null;
String token = null;
if (requestToken != null && requestToken.startsWith("Bearer")) {
token = requestToken.substring(7);
try {
username = this.jwtTokenHelper.getUsernameFromToken(token);
} catch (IllegalArgumentException e) {
System.out.println("Unable to get Jwt token");
} catch (ExpiredJwtException e) {
System.out.println("Jwt token has expired");
} catch (MalformedJwtException e) {
System.out.println("invalid jwt");
}
} else {
System.out.println("Jwt token does not begin with Bearer");
}
// once we get the token , now validate
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
if (this.jwtTokenHelper.validateToken(token, userDetails)) {
// shi chal rha hai
// authentication karna hai
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
usernamePasswordAuthenticationToken
.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
} else {
System.out.println("Invalid jwt token");
}
} else {
System.out.println("username is null or context is not null");
}
filterChain.doFilter(request, response);
}
}
package com.mohammedkhadeer.blog;
import java.util.List;
import org.modelmapper.ModelMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import com.mohammedkhadeer.blog.config.AppConstants;
import com.mohammedkhadeer.blog.entities.Role;
import com.mohammedkhadeer.blog.repositories.RoleRepo;
#SpringBootApplication
public class BlogAppApisApplication implements CommandLineRunner {
#Autowired
private PasswordEncoder passwordEncoder;
#Autowired
private RoleRepo roleRepo;
public static void main(String[] args) {
SpringApplication.run(BlogAppApisApplication.class, args);
}
#Bean
public ModelMapper modelMapper() {
return new ModelMapper();
}
#Override
public void run(String... args) throws Exception {
System.out.println(this.passwordEncoder.encode("xyz"));
try {
Role role = new Role();
role.setId(AppConstants.ADMIN_USER);
role.setName("ROLE_ADMIN");
Role role1 = new Role();
role1.setId(AppConstants.NORMAL_USER);
role1.setName("ROLE_NORMAL");
List<Role> roles = List.of(role, role1);
List<Role> result = this.roleRepo.saveAll(roles);
result.forEach(r -> {
System.out.println(r.getName());
});
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
The title is self explanatory, I have this API made in Spring, and after JWT implementation I can login, but the gets doesn't work anymore.
But if I comment the the CorsConfigurationSource corsConfigurationSource() and the Lombok annotation #RequiredArgsConstructor in SecurityConfig, the login stop to work, but all the gets work beautifully.
I'll post the class SecurityConfig, AuthorizationFilter and the AuthenticationFilter respectively for you guys try to see whats going wrong.
import org.alterdata.shopback.app.security.AuthenticationFilter;
import org.alterdata.shopback.app.security.AuthorizationFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import lombok.RequiredArgsConstructor;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import java.util.Arrays;
#Configuration
#EnableWebSecurity
#RequiredArgsConstructor
public class SecurityConfig extends WebSecurityConfigurerAdapter{
#Autowired
UserDetailsService userDetailsService;
#Autowired
BCryptPasswordEncoder bCryptPasswordEncoder;
#Override
protected void configure(HttpSecurity http) throws Exception {
AuthenticationFilter authenticationFilter = new AuthenticationFilter(authenticationManagerBean());
http.cors().and().csrf().disable();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.authorizeRequests().antMatchers("/login").permitAll()
.antMatchers("/cadastrar/**").hasAnyAuthority("ADMIN")
.antMatchers("/cadastro/**").hasAnyAuthority("ADMIN")
.anyRequest().authenticated();
http.addFilter(authenticationFilter);
http.addFilterBefore(new AuthorizationFilter(), UsernamePasswordAuthenticationFilter.class);
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder);
}
#Bean
#Override
public AuthenticationManager authenticationManager()throws Exception{
return super.authenticationManager();
}
#Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("http://localhost:3000"));
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE","OPTIONS", "HEAD"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Arrays;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.auth0.jwt.exceptions.JWTVerificationException;
import io.jsonwebtoken.JwtException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.OncePerRequestFilter;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.auth0.jwt.interfaces.JWTVerifier;
import com.fasterxml.jackson.databind.ObjectMapper;
import net.bytebuddy.implementation.bind.annotation.IgnoreForBinding.Verifier;
import static org.springframework.util.MimeTypeUtils.APPLICATION_JSON_VALUE;
#Slf4j
public class AuthorizationFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
if(request.getServletPath().equals("/login")) {
filterChain.doFilter(request, response);
}else {
String authorizationHeader = request.getHeader(HttpHeaders.AUTHORIZATION);
if(authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
try {
String token = authorizationHeader.substring("Bearer ".length());
Algorithm algorithm = Algorithm.HMAC256("segredinho".getBytes());
JWTVerifier jwtVerifier = JWT.require(algorithm).build();
DecodedJWT decodedJWT = jwtVerifier.verify(token);
String user = decodedJWT.getSubject();
String [] roles = decodedJWT.getClaim("roles").asArray(String.class);
Collection<SimpleGrantedAuthority> authorities = new ArrayList<>();
Arrays.stream(roles).forEach(role ->{
authorities.add(new SimpleGrantedAuthority(role));
});
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(user,null, authorities);
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
filterChain.doFilter(request, response);
}catch(Exception e){
log.error("erro ao realizar o login! {}", e.getMessage());
response.setHeader("erro", e.getMessage());
response.setStatus(401);
Map<String, String> error = new HashMap<>();
error.put("mensagem de erro", e.getMessage());
response.setContentType(APPLICATION_JSON_VALUE);
new ObjectMapper().writeValue(response.getOutputStream(), error);
}
}else {
filterChain.doFilter(request, response);
}
}
}
}
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.MediaType;
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.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import org.springframework.web.bind.annotation.CrossOrigin;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
#CrossOrigin(origins = {"*"})
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{
String nome = request.getParameter("nome");
String senha = request.getParameter("senha");
UsernamePasswordAuthenticationToken uspsToken = new UsernamePasswordAuthenticationToken(nome, senha);
//response.setHeader("teste", String.valueOf(uspsToken));
return authenticationManager.authenticate(uspsToken);
}catch (Exception e){
throw new RuntimeException();
}
}
// #Override
// public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
// throws AuthenticationException {
// try{
// UserPasswordAuthRequest userPasswordAuthRequest = new ObjectMapper()
// .readValue(request.getInputStream(), UserPasswordAuthRequest.class);
//
// Authentication authentication = new UsernamePasswordAuthenticationToken(
// userPasswordAuthRequest.getNome(),
// userPasswordAuthRequest.getSenha()
// );
// return authenticationManager.authenticate(authentication);
// }catch (IOException e){
// throw new RuntimeException();
// }
// }
#Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
Authentication authResult) throws IOException, ServletException {
User user = (User) authResult.getPrincipal();
Algorithm algorithm = Algorithm.HMAC256("segredinho".getBytes());
String tokenAcesso = JWT.create().withSubject(user.getUsername())
.withExpiresAt(new Date(System.currentTimeMillis() + 10*60*1000*60))
.withIssuer(request.getRequestURL()
.toString()).withClaim("roles", user.getAuthorities()
.stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList()))
.sign(algorithm);
String tokenRefresh = JWT.create().withSubject(user.getUsername())
.withExpiresAt(new Date(System.currentTimeMillis() + 10*60*1000*60))
.withIssuer(request.getRequestURL()
.toString()).withClaim("roles", user.getAuthorities()
.stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList()))
.sign(algorithm);
Map<String, String> tokens = new HashMap<>();
tokens.put("tokenacesso", tokenAcesso);
tokens.put("tokenrefresh", tokenRefresh);
response.setContentType(APPLICATION_JSON_VALUE);
new ObjectMapper().writeValue(response.getOutputStream(),tokens);
}
}
First try to call your api with Postman, else i think you need to add authorization jwt header to every api request after you login.
export default function authHeader() {
const user = JSON.parse(localStorage.getItem('user'));
if (user && user.accessToken) {
return { Authorization: 'Bearer ' + user.accessToken };
} else {
return {};
}
}
and then call your api like this:
return axios.get(API_URL + 'user', { headers: authHeader() });
for more informations try to visit: https://www.bezkoder.com/react-jwt-auth/
I just fixed using this global cors
#Bean
public CorsConfigurationSource corsConfigurationSource() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());
return source;
}
I'm writing the JWTLoginFilter for check the user and authenticate it, but I can't make the request to DB.
This is the code of my Login filter:
import com.fasterxml.jackson.databind.ObjectMapper;
import it.gate42.Model.Credential;
import it.gate42.Model.User;
import it.gate42.repository.User.UserRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Collections;
public class JWTLoginFilter extends AbstractAuthenticationProcessingFilter {
private static final Logger LOGGER = LoggerFactory.getLogger(JWTLoginFilter.class);
#Autowired
UserRepository userRepository;
public JWTLoginFilter(String url, AuthenticationManager authManager) {
super(new AntPathRequestMatcher(url));
setAuthenticationManager(authManager);
}
#Override
public Authentication attemptAuthentication(HttpServletRequest req, HttpServletResponse res) throws AuthenticationException, IOException, ServletException {
Credential creds = new ObjectMapper().readValue(req.getInputStream(), Credential.class);
User user = null;
user = userRepository.login(creds);
return new UsernamePasswordAuthenticationToken(
creds.getUsername(),
creds.getPassword()
);
/*return getAuthenticationManager().authenticate(
new UsernamePasswordAuthenticationToken(
creds.getUsername(),
creds.getPassword(),
Collections.emptyList()
)
);*/
}
#Override
protected void successfulAuthentication(HttpServletRequest req, HttpServletResponse res, FilterChain chain, Authentication auth) throws IOException, ServletException {
LOGGER.info("a: " + auth.toString());
TokenAuthenticationService.addAuthentication(res, auth.getName());
}
}
Any function call on userRepository I receive a:
java.lang.NullPointerException: null
at it.gate42.Config.Security.JWTLoginFilter.attemptAuthentication
How I can communicate to my DB for check the user? I'm using Spring boot, Jersey, JWT, Mongo and Spring security for filter the route.
In this same class JWTLoginFilter, add code below:
#Bean
public UserRepository userRepository() {
return new UserRepository();
}
In the same class, remove this code:
#Autowired
UserRepository userRepository;
In the method attemptAuthentication(), change the repository:
user = userRepository().login(creds);