io.jsonwebtoken.SignatureException: JWT signature does not match locally computed signature - spring-boot

I am using spring boot and spring security with JWT on rest service. I used the code in below link:
https://www.javainuse.com/spring/boot-jwt-mysql
And it works fine but when we use its token for authentication and ... We face the below error:
io.jsonwebtoken.SignatureException: JWT signature does not match locally computed signature. JWT validity cannot be asserted and should not be trusted.
at io.jsonwebtoken.impl.DefaultJwtParser.parse(DefaultJwtParser.java:354) ~[jjwt-0.9.1.jar:0.9.1]
at io.jsonwebtoken.impl.DefaultJwtParser.parse(DefaultJwtParser.java:481) ~[jjwt-0.9.1.jar:0.9.1]
at io.jsonwebtoken.impl.DefaultJwtParser.parseClaimsJws(DefaultJwtParser.java:541) ~[jjwt-0.9.1.jar:0.9.1]
at com.isan.config.JwtTokenUtil.getAllClaimsFromToken(JwtTokenUtil.java:46) ~[classes/:na]
at com.isan.config.JwtTokenUtil.getClaimFromToken(JwtTokenUtil.java:41) ~[classes/:na]
at com.isan.config.JwtTokenUtil.getUsernameFromToken(JwtTokenUtil.java:32) ~[classes/:na]
at com.isan.config.JwtRequestFilter.doFilterInternal(JwtRequestFilter.java:44) ~[classes/:na]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.1.jar:5.3.1]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-5.4.1.jar:5.4.1]
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:103) ~[spring-security-web-5.4.1.jar:5.4.1]
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:89) ~[spring-security-web-5.4.1.jar:5.4.1]
These are the codes:
1-WebSecurityConfig class:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
#Autowired
private UserDetailsService jwtUserDetailsService;
#Autowired
private JwtRequestFilter jwtRequestFilter;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
// configure AuthenticationManager so that it knows from where to load
// user for matching credentials
// Use BCryptPasswordEncoder
auth.userDetailsService(jwtUserDetailsService).passwordEncoder(passwordEncoder());
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
// We don't need CSRF for this example
httpSecurity.csrf().disable()
// dont authenticate this particular request
.authorizeRequests().antMatchers("/authenticate", "/register").permitAll().
// all other requests need to be authenticated
anyRequest().authenticated().and().
// make sure we use stateless session; session won't be used to
// store user's state.
exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint).and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
// Add a filter to validate the tokens with every request
httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
}
}
2-JwtTokenUtil class:
#Component
public class JwtTokenUtil implements Serializable {
private static final long serialVersionUID = -2550185165626007488L;
public static final long JWT_TOKEN_VALIDITY = 5 * 60 * 60;
#Value("${jwt.secret}")
private String secret;
//retrieve username from jwt token
public String getUsernameFromToken(String token) {
return getClaimFromToken(token, Claims::getSubject);
}
//retrieve expiration date from jwt token
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);
}
//for retrieveing any information from token we will need the secret key
private Claims getAllClaimsFromToken(String token) {
return Jwts.parser().setSigningKey(secret.getBytes()).parseClaimsJws(token).getBody();
}
//check if the token has expired
private Boolean isTokenExpired(String token) {
final Date expiration = getExpirationDateFromToken(token);
return expiration.before(new Date());
}
//generate token for user
public String generateToken(UserDetails userDetails) {
Map<String, Object> claims = new HashMap<>();
return doGenerateToken(claims, userDetails.getUsername());
}
//while creating the token -
//1. Define claims of the token, like Issuer, Expiration, Subject, and the ID
//2. Sign the JWT using the HS512 algorithm and secret key.
//3. According to JWS Compact Serialization(https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-41#section-3.1)
// compaction of the JWT to a URL-safe string
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(SignatureAlgorithm.HS512, secret.getBytes()).compact();
}
//validate token
public Boolean validateToken(String token, UserDetails userDetails) {
final String username = getUsernameFromToken(token);
return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
}
}
3-JwtRequestFilter class:
#Component
public class JwtRequestFilter extends OncePerRequestFilter {
#Autowired
private JwtUserDetailsService jwtUserDetailsService;
#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;
// JWT Token is in the form "Bearer token". Remove Bearer word and get
// only the Token
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");
}
// Once we get the token validate it.
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = this.jwtUserDetailsService.loadUserByUsername(username);
// if token is valid configure Spring Security to manually set
// authentication
if (jwtTokenUtil.validateToken(jwtToken, userDetails)) {
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
usernamePasswordAuthenticationToken
.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
// After setting the Authentication in the context, we specify
// that the current user is authenticated. So it passes the
// Spring Security Configurations successfully.
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
}
}
chain.doFilter(request, response);
}
}
4-JwtAuthenticationEntryPoint class:
#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");
}
}
5-JwtAuthenticationController class:
#RestController
#CrossOrigin
public class JwtAuthenticationController {
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private JwtTokenUtil jwtTokenUtil;
#Autowired
private JwtUserDetailsService userDetailsService;
#RequestMapping(value = "/authenticate", method = RequestMethod.POST)
public ResponseEntity<?> createAuthenticationToken(#RequestBody JwtRequest authenticationRequest) throws Exception {
authenticate(authenticationRequest.getUsername(), authenticationRequest.getPassword());
final UserDetails userDetails = userDetailsService
.loadUserByUsername(authenticationRequest.getUsername());
final String token = jwtTokenUtil.generateToken(userDetails);
return ResponseEntity.ok(new JwtResponse(token));
}
#RequestMapping(value = "/register", method = RequestMethod.POST)
public ResponseEntity<?> saveUser(#RequestBody UserDTO user) throws Exception {
return ResponseEntity.ok(userDetailsService.save(user));
}
private void authenticate(String username, String password) throws Exception {
try {
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
} catch (DisabledException e) {
throw new Exception("USER_DISABLED", e);
} catch (BadCredentialsException e) {
throw new Exception("INVALID_CREDENTIALS", e);
}
}
}
6-JwtUserDetailsService class:
#Service
public class JwtUserDetailsService implements UserDetailsService {
#Autowired
private UserDao userDao;
#Autowired
private PasswordEncoder bcryptEncoder;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
DAOUser user = userDao.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("User not found with username: " + username);
}
return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(),
new ArrayList<>());
}
public DAOUser save(UserDTO user) {
DAOUser newUser = new DAOUser();
newUser.setUsername(user.getUsername());
newUser.setPassword(bcryptEncoder.encode(user.getPassword()));
return userDao.save(newUser);
}
}
7- application.properties
jwt.secret=javainuse
spring.datasource.url=jdbc:mysql://localhost/bootdb?createDatabaseIfNotExist=true&autoReconnect=true&useSSL=false
spring.datasource.username=root
spring.datasource.password=asdasdasd
spring.datasource.platform=mysql
spring.jpa.hibernate.ddl-auto=create-drop
8-pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.javainuse</groupId>
<artifactId>spring-boot-jwt</artifactId>
<version>0.0.1-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.1.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
</project>
9-token example:
eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJzYW0iLCJleHAiOjE2MDgwNjM1NTUsImlhdCI6MTYwODA0NTU1NX0.X77D8mceGbBWRCWUOwhL-5iwpZQqFAITmQqOW_s48vGDkkWdMFg1dm_Gackri8evPVzkF6lt2tXvHCbOlpe86w
10-secret example:
javainuse

In JwtTokenUtil class, just change below methods:
private Claims getAllClaimsFromToken(String token) {
return Jwts.parser().setSigningKey(secret.getBytes(Charset.forName("UTF-8"))).parseClaimsJws(token.replace("{", "").replace("}","")).getBody();
}
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(SignatureAlgorithm.HS512, secret.getBytes(Charset.forName("UTF-8"))).compact();
}

First thing, we need verify our Auth token, while generating auth token in case of signup or signin API call we should not pass the token in postman request headers. Please be sure in Authorisation tab select type as "No Auth" for above APIs I mentioned.

Related

JWT authorization on spring security is throwing 403 Forbidden request

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.

Jwt login with spring boot and angular

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?

Spring Boot Jwt returns access denied

Hey everyone i have problem with jwt with Java.Here is the codes.
Here is returned value from postman
{
"timestamp": "2020-02-29T20:53:35.761+0000",
"status": 403,
"error": "Forbidden",
"message": "Access Denied",
"path": "/login"
}
TokenManager.java
#Service
public class TokenManager {
private static final int expiredAt = 10 * 60 * 60 * 1000;
Key key = Keys.secretKeyFor(SignatureAlgorithm.HS256);
public String generateToken(String username){
return Jwts.builder().setSubject(username)
.setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + expiredAt))
.signWith(key).compact();
}
public boolean tokenValidate(String token){
if(getUserFromToken(token) != null && isExpired(token)) {
return true;
}
return false;
}
public String getUserFromToken(String token){
Claims claims = getClaims(token);
return claims.getSubject();
}
public boolean isExpired(String token){
Claims claims = getClaims(token);
return claims.getExpiration().after(new Date(System.currentTimeMillis()));
}
private Claims getClaims(String token) {
return Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token).getBody();
}
}
And then JwtTokenFilter.java
#Component
public class JwtTokenFilter extends OncePerRequestFilter {
#Autowired
private TokenManager tokenManager;
#Override
protected void doFilterInternal(HttpServletRequest httpServletRequest,
#NotNull HttpServletResponse httpServletResponse,
#NotNull FilterChain filterChain) throws ServletException, IOException {
final String authHeader = httpServletRequest.getHeader("Authorization");
String username = null;
String token = null;
if (authHeader != null && authHeader.contains("Bearer")) {
token = authHeader.substring(7);
try {
username = tokenManager.getUserFromToken(token);
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
if (username != null && token != null
&& SecurityContextHolder.getContext().getAuthentication() == null) {
if (tokenManager.tokenValidate(token)) {
UsernamePasswordAuthenticationToken upassToken =
new UsernamePasswordAuthenticationToken(username, null, new ArrayList<>());
upassToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest));
SecurityContextHolder.getContext().setAuthentication(upassToken);
}
}
filterChain.doFilter(httpServletRequest, httpServletResponse);
}
}
And my custom UserDetailService
#Service
public class CustomUserDetailsService implements org.springframework.security.core.userdetails.UserDetailsService {
#Autowired
private UserRepository userRepository;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return userRepository.findByUsername(username);
}
}
Here is WebSecurityConfig
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private JwtTokenFilter tokenFilter;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests().antMatchers("/signup","/login").permitAll()
.anyRequest().authenticated();
http.addFilterBefore(tokenFilter, UsernamePasswordAuthenticationFilter.class);
}
#Bean
public AuthenticationManager getAuthenticationManager() throws Exception {
return super.authenticationManagerBean();
}
}
And last one is my controller.I checked the request body and and print the data it just work fine but /login path returns access denied.
#RestController
public class UserController {
#Autowired
private UserService userService;
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private TokenManager tokenManager;
public UserController(UserService userService, AuthenticationManager authenticationManager, TokenManager tokenManager) {
this.userService = userService;
this.authenticationManager = authenticationManager;
this.tokenManager = tokenManager;
}
#RequestMapping(value = "/signup", method = RequestMethod.POST)
public ResponseEntity<User> signup(#RequestBody User user){
return ResponseEntity.ok(userService.save(user));
}
#RequestMapping(value = "/login", method = RequestMethod.POST)
public ResponseEntity<String> login(#Valid #RequestBody AuthRequest authRequest){
try{
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(authRequest.getUsername(),authRequest.getPassword()));
return ResponseEntity.ok(tokenManager.generateToken(authRequest.getUsername()));
}catch (Exception e){
throw e;
}
}
}
When I remove authenticationManager.authenticate method inside login function it returns a valid token.But when I add authenticationManager again it returns access denied.
Actually you did not setup the AuthenticationManager properly.
in your code, you just used the default authentication manager. And it is ok, as there is one default implementation shipped in Spring boot security, which is ProviderManager. what [ProviderManager][1] does is:
Iterates an Authentication request through a list of AuthenticationProviders.
So you need at least one AuthenticationProvider
There are quite some AuthenticationProviders, for example:
AnonymousAuthenticationProvider, NullAuthenticationProvider, DaoAuthenticationProvider, LdapAuthenticationProvider etc
And in your case, you are authenticating against database, so the DaoAuthenticationProvider is the choice.
And Spring security has a very easy way to configure the DaoAuthenticationProvider, and actually, it automatically created one for you when you set userDetailsService to the AuthenticationManagerBuilder to configure your AuthenticationManager, code like this:
#Autowired
private CustomUserDetailsService userDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
so all you need to do is add the code snipet above to your WebSecurityConfig
And it is also recommended to use PasswordEncoder instead of storing your password as plain text. A simple way is to use BCryptPasswordEncoder to encode your password before save the user to db...
#Autowired
private CustomUserDetailsService userDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
#Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}

Spring Security JWT Example without spring boot

I have a Spring 5 MVC/REST application. i using JPA Hibernate as the ORM.
I want to secure my application. All examples I see are based on Spring Boot.
I want use jwt in my application. I want without XML Config too.
( Spring MVC - 5.2.0.RELEASE )
I need Example. Thanks
To secure a Spring REST application using Spring Security and JWT, you can follow the below steps
Add the following dependencies in pom.xml
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
Create JwtAuthenticationEntryPoint to rejet every unauthenticated request
#Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint, Serializable {
#Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
}
}
Create model classes
public class JwtRequest implements Serializable {
private static final long serialVersionUID = 5926468583005150707L;
private String username;
private String password;
// need default constructor for JSON Parsing
public JwtRequest() {
}
public JwtRequest(String username, String password) {
this.setUsername(username);
this.setPassword(password);
}
// Getters and Setters
}
public class JwtResponse implements Serializable {
private static final long serialVersionUID = -8091879091924046844L;
private final String jwttoken;
public JwtResponse(String jwttoken) {
this.jwttoken = jwttoken;
}
public String getToken() {
return this.jwttoken;
}
}
Create Util class for token generation and validation
#Component
public class JwtTokenUtil implements Serializable {
private static final long serialVersionUID = -2550185165626007488L;
public static final long JWT_TOKEN_VALIDITY = 5 * 60 * 60;
#Value("${jwt.secret}")
private String secret;
// retrieve username from jwt token
public String getUsernameFromToken(String token) {
return getClaimFromToken(token, Claims::getSubject);
}
// retrieve expiration date from jwt token
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);
}
// for retrieveing any information from token we will need the secret key
private Claims getAllClaimsFromToken(String token) {
return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
}
// check if the token has expired
private Boolean isTokenExpired(String token) {
final Date expiration = getExpirationDateFromToken(token);
return expiration.before(new Date());
}
// generate token for user
public String generateToken(UserDetails userDetails) {
Map<String, Object> claims = new HashMap<>();
return doGenerateToken(claims, userDetails.getUsername());
}
// while creating the token -
// 1. Define claims of the token, like Issuer, Expiration, Subject, and the ID
// 2. Sign the JWT using the HS512 algorithm and secret key.
// 3. According to JWS Compact
// Serialization(https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-41#section-3.1)
// compaction of the JWT to a URL-safe string
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(SignatureAlgorithm.HS512, secret).compact();
}
// validate token
public Boolean validateToken(String token, UserDetails userDetails) {
final String username = getUsernameFromToken(token);
return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
}
}
Create UserDetailsService and UserRepository implementations to load the user by user name from database
#Service
public class JwtUserDetailsService implements UserDetailsService {
#Autowired
private UserRepository repo;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserEntity user = repo.findByUsername(username);
if (user != null) {
return new User(user.getUsername(), user.getPassword(), new ArrayList<>());
} else {
throw new UsernameNotFoundException("User not found with username: " + username);
}
}
}
Create a reqeust filter to check if the request has a valid JWT token. If it has a valid JWT Token then set the Authentication in the context, to specify that the current user is authenticated.
#Component
public class JwtRequestFilter extends OncePerRequestFilter {
#Autowired
private JwtUserDetailsService jwtUserDetailsService;
#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;
// JWT Token is in the form "Bearer token". Remove Bearer word and get
// only the Token
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");
}
// Once we get the token validate it.
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = this.jwtUserDetailsService.loadUserByUsername(username);
// if token is valid configure Spring Security to manually set
// authentication
if (jwtTokenUtil.validateToken(jwtToken, userDetails)) {
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
// After setting the Authentication in the context, we specify
// that the current user is authenticated. So it passes the
// Spring Security Configurations successfully.
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
}
}
chain.doFilter(request, response);
}
}
Create an authentication controller to expose a POST API /authenticate which accepts username and password in the body. Using Spring Authentication Manager we authenticate the username and password. If the credentials are valid, a JWT token is created using the JwtTokenUtil and provided to the client.
#RestController
#CrossOrigin
public class JwtAuthenticationController {
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private JwtTokenUtil jwtTokenUtil;
#Autowired
private JwtUserDetailsService userDetailsService;
#PostMapping("/authenticate")
public ResponseEntity<?> createAuthenticationToken(#RequestBody JwtRequest authenticationRequest) throws Exception {
authenticate(authenticationRequest.getUsername(), authenticationRequest.getPassword());
final UserDetails userDetails = userDetailsService.loadUserByUsername(authenticationRequest.getUsername());
final String token = jwtTokenUtil.generateToken(userDetails);
return ResponseEntity.ok(new JwtResponse(token));
}
private void authenticate(String username, String password) throws Exception {
try {
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
} catch (DisabledException e) {
throw new Exception("USER_DISABLED", e);
} catch (BadCredentialsException e) {
throw new Exception("INVALID_CREDENTIALS", e);
}
}
}
Configure Spring Security
#Configuration
#ComponentScan(basePackages = "com.javachinna")
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
#Autowired
private UserDetailsService jwtUserDetailsService;
#Autowired
private JwtRequestFilter jwtRequestFilter;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
// configure AuthenticationManager so that it knows from where to load
// user for matching credentials
// Use BCryptPasswordEncoder
auth.userDetailsService(jwtUserDetailsService).passwordEncoder(passwordEncoder());
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
// We don't need CSRF for this example
httpSecurity.csrf().disable()
// dont authenticate this particular request
.authorizeRequests().antMatchers("/authenticate").permitAll().
// all other requests need to be authenticated
anyRequest().authenticated().and().
// make sure we use stateless session; session won't be used to
// store user's state.
exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint).and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
// Add a filter to validate the tokens with every request
httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
}
}
Create SecurityWebApplicationInitializer to register the springSecurityFilterChain
public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer {
}
Add the WebSecurityConfig class to the existing ApplicationInitializer
public class SpringWebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class[] getRootConfigClasses() {
return new Class[] { WebSecurityConfig.class };
}
// ... other overrides ...
}
Add the secret key to the application.properties file
App Properties
jwt.secret=javachinna
That's it. Now generate a JSON Web Token by creating a POST request with url localhost:8080/authenticate and valid username and password in the request body. Use this token in the request header for each request.
For more information, refer the article on Securing Spring REST services using JWT without using spring boot.
I write a sample for use Spring security 5.0.0 none boot and with JWT, dynamic Role.
I hope it's good
Spring MVC 5.0.0
Spring Security 5.0.0
hibernate JPA 5.2.11
and my DB is Oracle DB
you can download it :
download from github

Spring security with JWT always returns 401 unauthorized

1
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Resource(name = "userService")
private UserDetailsService userDetailsService;
#Autowired
private JwtAuthenticationEntryPoint unauthorizedHandler;
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Autowired
public void globalUserDetails(AuthenticationManagerBuilder auth, DataSource dataSource) throws Exception {
/*auth.jdbcAuthentication()
.dataSource(dataSource)
.usersByUsernameQuery("select login as principal, mot_de_passe as credentials, flag_compte_actif as enabled from utilisateur where login = ?")
.authoritiesByUsernameQuery("SELECT utilisateur.login as principal, profil.designation as role FROM utilisateur INNER JOIN user_profil ON utilisateur.id_user = user_profil.iduserpk INNER JOIN profil ON user_profil.idprofilpk = profil.id_profil WHERE utilisateur.login = ? ")
.rolePrefix("ROLE_");
auth.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());*/
auth.inMemoryAuthentication()
.withUser("admin")
.password("password")
.roles("Administrateur");
}
#Bean
public JwtAuthenticationFilter authenticationTokenFilterBean() throws Exception {
return new JwtAuthenticationFilter();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
System.out.println("Akal configure method begin");
//http.addFilterBefore(new CorsFilter(), ChannelProcessingFilter.class);
http.cors().and()
.csrf().disable().
authorizeRequests()
.antMatchers("/token/generate").permitAll()
.anyRequest().authenticated()
.and().formLogin().disable()
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
System.out.println("Akal configure method");
http
.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
}
// #Bean
// public BCryptPasswordEncoder passwordEncoder(){
// return new BCryptPasswordEncoder();
// }
#Bean
public static NoOpPasswordEncoder passwordEncoder() {
return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance();
}
}
2
#RestController
#CrossOrigin("*")
public class AuthenticationController {
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private JwtTokenUtil jwtTokenUtil;
#Autowired
private UtilisateurRepository userRepo;
#PostMapping(value = "/token/generate")
public ResponseEntity<?> register(#RequestBody LoginUser loginUser) throws AuthenticationException {
System.out.println("We're in man!");
final Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
loginUser.getUsername(),
loginUser.getPassword()
)
);
System.out.println("(Username, Password): (" + loginUser.getUsername() + ", " + loginUser.getPassword() + ")");
SecurityContextHolder.getContext().setAuthentication(authentication);
final Utilisateur user = userRepo.findByLogin(loginUser.getUsername());
final String token = jwtTokenUtil.generateToken(user);
System.out.println("Token Controller Access=> Token Generated: " + token);
return ResponseEntity.ok(new AuthToken(token));
}
}
3
public class AuthToken {
private String token;
public AuthToken(){
}
public AuthToken(String token){
this.token = token;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
}
4
public class CorsFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
System.out.println("Filtering on...........................................................");
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, OPTIONS, DELETE");
//response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "X-Requested-With, Content-Type, Authorization, Origin, Accept, Access-Control-Request-Method, Access-Control-Request-Headers");
chain.doFilter(req, res);
}
public void init(FilterConfig filterConfig) {}
public void destroy() {}
}
5
#Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint, Serializable {
#Override
public void commence(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException authException) throws IOException {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
}
}
6
public class JwtAuthenticationFilter extends OncePerRequestFilter {
#Autowired
private UserDetailsService userDetailsService;
#Autowired
private JwtTokenUtil jwtTokenUtil;
#Override
protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws IOException, ServletException {
String header = req.getHeader("Authorization");
String username = null;
String authToken = null;
if (header != null && header.startsWith("Bearer ")) {
authToken = header.replace("Bearer ","");
try {
username = jwtTokenUtil.getUsernameFromToken(authToken);
} catch (IllegalArgumentException e) {
logger.error("an error occured during getting username from token", e);
} catch (ExpiredJwtException e) {
logger.warn("the token is expired and not valid anymore", e);
} catch(SignatureException e){
logger.error("Authentication Failed. Username or Password not valid.");
}
} else {
logger.warn("couldn't find bearer string, will ignore the header");
}
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
if (jwtTokenUtil.validateToken(authToken, userDetails)) {
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, Arrays.asList(new SimpleGrantedAuthority("ROLE_ADMIN")));
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(req));
logger.info("authenticated user " + username + ", setting security context");
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
chain.doFilter(req, res);
}
}
7
#Component
public class JwtTokenUtil implements Serializable {
static final long EXPIRATIONTIME = 864_000_000; // 10 days
static final String SECRET = "secret";
static final String TOKEN_PREFIX = "Bearer";
static final String HEADER_STRING = "Authorization";
public String getUsernameFromToken(String token) {
return getClaimFromToken(token, Claims::getSubject);
}
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.parser()
.setSigningKey(SECRET)
.parseClaimsJws(token)
.getBody();
}
private Boolean isTokenExpired(String token) {
final Date expiration = getExpirationDateFromToken(token);
return expiration.before(new Date());
}
public String generateToken(Utilisateur user) {
return doGenerateToken(user.getLogin());
}
private String doGenerateToken(String subject) {
Claims claims = Jwts.claims().setSubject(subject);
claims.put("scopes", Arrays.asList(new SimpleGrantedAuthority("ROLE_Administrateur")));
return Jwts.builder()
.setClaims(claims)
.setIssuer("http://devglan.com")
.setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATIONTIME))
.signWith(SignatureAlgorithm.HS256, SECRET)
.compact();
}
public Boolean validateToken(String token, UserDetails userDetails) {
final String username = getUsernameFromToken(token);
return (
username.equals(userDetails.getUsername())
&& !isTokenExpired(token));
}
}
8
public class LoginUser {
private String username;
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
I only posted these 2 classes, because honestly I have 8 configuration classes, it's gonna be a pain to read! And it's custom JWT code too, but if it's necessary to post it all, let me know.
Other than that, I just cannot identify the problem! Spring console doesn't show any errors whatsoever and when I try to request from Postman, here the outcome:
result
And when I run the request from the browser, it doesn't say 401, it just says bad credentials even though they're correct and I tried with dozens of users too to make sure
Thank you!
Update: I posted the rest of the classes because the problem might not be related to just these 2
In Spring Security 5, if you are using auth.inMemoryAuthentication(), you won't be able to use BCryptPasswordEncoder or StandardPasswordEncoder. You must use your own UserDetailsService in order to get a user and password. Or if you need to test your code, just return NoOpPasswordEncoder.getInstance() in your passwordEncoder() method
SecurityConfig.class
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private AccountService accountService; //your own implementation of UserDetailsService
....
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.eraseCredentials(true)
.userDetailsService(accountService)
.passwordEncoder(passwordEncoder());
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
....
}
AccountService.class
#Service
#Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
public class AccountService implements UserDetailsService {
#Autowired
private AccountRepository accountRepository; //Your database repository
#Autowired
private PasswordEncoder passwordEncoder;
#PostConstruct
protected void initialize() {
save(new Account("user", "demo", "ROLE_USER"));
save(new Account("admin", "admin", "ROLE_ADMIN"));
}
#Transactional
public Account save(Account account) {
account.setPassword(passwordEncoder.encode(account.getPassword()));
accountRepository.save(account);
return account;
}
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Account account = accountRepository.findOneByEmail(username);
if(account == null) {
throw new UsernameNotFoundException("user not found");
}
return createUser(account);
}
public void signin(Account account) {
SecurityContextHolder.getContext().setAuthentication(authenticate(account));
}
private Authentication authenticate(Account account) {
return new UsernamePasswordAuthenticationToken(createUser(account), null, Collections.singleton(createAuthority(account)));
}
private User createUser(Account account) {
return new User(account.getEmail(), account.getPassword(), Collections.singleton(createAuthority(account)));
}
private GrantedAuthority createAuthority(Account account) {
return new SimpleGrantedAuthority(account.getRole());
}
}
Account.class
#SuppressWarnings("serial")
#Entity
#Table(name = "account")
public class Account implements java.io.Serializable {
#Id
#GeneratedValue
private Long id;
#Column(unique = true)
private String email;
#JsonIgnore
private String password;
private String role = "ROLE_USER";
private Instant created;
protected Account() {
}
public Account(String email, String password, String role) {
this.email = email;
this.password = password;
this.role = role;
this.created = Instant.now();
}
public Long getId() {
return id;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
public Instant getCreated() {
return created;
}
}
You are not using PasswordEncoder in your globalUserDetails() method. Spring security by default take encoded password. Your code should be like.
#Autowired
public void globalUserDetails(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().passwordEncoder(passwordEncoder)
.withUser("admin")
.password("password")
.roles("Admin");
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
You are using do not need dataSource because you are using inMemoryAuthenticatin().

Resources