Use claims auth
This commit is contained in:
@@ -24,7 +24,6 @@ import org.springframework.security.authentication.InternalAuthenticationService
|
|||||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
import org.springframework.security.core.userdetails.UserDetails;
|
|
||||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
@@ -89,13 +88,7 @@ public class AuthController {
|
|||||||
// Create or link customer record
|
// Create or link customer record
|
||||||
userBusinessLinkageService.ensureLinkedCustomer(savedUser);
|
userBusinessLinkageService.ensureLinkedCustomer(savedUser);
|
||||||
|
|
||||||
UserDetails userDetails = new org.springframework.security.core.userdetails.User(
|
String token = jwtUtil.generateToken(savedUser);
|
||||||
savedUser.getUsername(),
|
|
||||||
savedUser.getPassword(),
|
|
||||||
java.util.Collections.emptyList()
|
|
||||||
);
|
|
||||||
|
|
||||||
String token = jwtUtil.generateToken(userDetails);
|
|
||||||
|
|
||||||
return ResponseEntity.status(HttpStatus.CREATED).body(new RegisterResponse(
|
return ResponseEntity.status(HttpStatus.CREATED).body(new RegisterResponse(
|
||||||
savedUser.getId(),
|
savedUser.getId(),
|
||||||
@@ -116,13 +109,7 @@ public class AuthController {
|
|||||||
User user = userRepository.findByUsername(request.getUsername())
|
User user = userRepository.findByUsername(request.getUsername())
|
||||||
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
|
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
|
||||||
|
|
||||||
UserDetails userDetails = new org.springframework.security.core.userdetails.User(
|
String token = jwtUtil.generateToken(user);
|
||||||
user.getUsername(),
|
|
||||||
user.getPassword(),
|
|
||||||
java.util.Collections.emptyList()
|
|
||||||
);
|
|
||||||
|
|
||||||
String token = jwtUtil.generateToken(userDetails);
|
|
||||||
|
|
||||||
return ResponseEntity.ok(new LoginResponse(
|
return ResponseEntity.ok(new LoginResponse(
|
||||||
token,
|
token,
|
||||||
|
|||||||
51
src/main/java/com/petshop/backend/security/AppPrincipal.java
Normal file
51
src/main/java/com/petshop/backend/security/AppPrincipal.java
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
package com.petshop.backend.security;
|
||||||
|
|
||||||
|
import com.petshop.backend.entity.User;
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
|
|
||||||
|
import java.security.Principal;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class AppPrincipal implements Principal {
|
||||||
|
|
||||||
|
private final Long userId;
|
||||||
|
private final String username;
|
||||||
|
private final User.Role role;
|
||||||
|
private final Integer tokenVersion;
|
||||||
|
private final List<GrantedAuthority> authorities;
|
||||||
|
|
||||||
|
public AppPrincipal(Long userId, String username, User.Role role, Integer tokenVersion) {
|
||||||
|
this.userId = userId;
|
||||||
|
this.username = username;
|
||||||
|
this.role = role;
|
||||||
|
this.tokenVersion = tokenVersion;
|
||||||
|
this.authorities = List.of(new SimpleGrantedAuthority("ROLE_" + role.name()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getUserId() {
|
||||||
|
return userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUsername() {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public User.Role getRole() {
|
||||||
|
return role;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getTokenVersion() {
|
||||||
|
return tokenVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection<? extends GrantedAuthority> getAuthorities() {
|
||||||
|
return authorities;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,15 +1,14 @@
|
|||||||
package com.petshop.backend.security;
|
package com.petshop.backend.security;
|
||||||
|
|
||||||
|
import com.petshop.backend.entity.User;
|
||||||
|
import com.petshop.backend.repository.UserRepository;
|
||||||
import jakarta.servlet.FilterChain;
|
import jakarta.servlet.FilterChain;
|
||||||
import jakarta.servlet.ServletException;
|
import jakarta.servlet.ServletException;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import org.springframework.lang.NonNull;
|
import org.springframework.lang.NonNull;
|
||||||
import org.springframework.security.authentication.DisabledException;
|
|
||||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
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.security.web.authentication.WebAuthenticationDetailsSource;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.web.filter.OncePerRequestFilter;
|
import org.springframework.web.filter.OncePerRequestFilter;
|
||||||
@@ -21,11 +20,11 @@ import java.time.LocalDateTime;
|
|||||||
public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
||||||
|
|
||||||
private final JwtUtil jwtUtil;
|
private final JwtUtil jwtUtil;
|
||||||
private final UserDetailsService userDetailsService;
|
private final UserRepository userRepository;
|
||||||
|
|
||||||
public JwtAuthenticationFilter(JwtUtil jwtUtil, UserDetailsService userDetailsService) {
|
public JwtAuthenticationFilter(JwtUtil jwtUtil, UserRepository userRepository) {
|
||||||
this.jwtUtil = jwtUtil;
|
this.jwtUtil = jwtUtil;
|
||||||
this.userDetailsService = userDetailsService;
|
this.userRepository = userRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -36,38 +35,47 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
|||||||
) throws ServletException, IOException {
|
) throws ServletException, IOException {
|
||||||
final String authHeader = request.getHeader("Authorization");
|
final String authHeader = request.getHeader("Authorization");
|
||||||
final String jwt;
|
final String jwt;
|
||||||
final String username;
|
|
||||||
|
|
||||||
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
|
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
|
||||||
filterChain.doFilter(request, response);
|
filterChain.doFilter(request, response);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
jwt = authHeader.substring(7);
|
jwt = authHeader.substring(7);
|
||||||
username = jwtUtil.extractUsername(jwt);
|
Long userId = jwtUtil.extractUserId(jwt);
|
||||||
|
|
||||||
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
|
if (userId != null && SecurityContextHolder.getContext().getAuthentication() == null) {
|
||||||
UserDetails userDetails;
|
User user = userRepository.findById(userId).orElse(null);
|
||||||
try {
|
if (user == null || user.getActive() == null || !user.getActive()) {
|
||||||
userDetails = userDetailsService.loadUserByUsername(username);
|
writeUnauthorized(response, "User account is inactive");
|
||||||
} catch (DisabledException ex) {
|
|
||||||
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
|
|
||||||
response.setContentType("application/json");
|
|
||||||
response.getWriter().write(
|
|
||||||
"{\"status\":401,\"message\":\"" + ex.getMessage() + "\",\"timestamp\":\"" + LocalDateTime.now() + "\"}"
|
|
||||||
);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (jwtUtil.validateToken(jwt, userDetails)) {
|
if (!jwtUtil.validateToken(jwt, user)) {
|
||||||
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
|
writeUnauthorized(response, "Invalid or expired token");
|
||||||
userDetails,
|
return;
|
||||||
null,
|
|
||||||
userDetails.getAuthorities()
|
|
||||||
);
|
|
||||||
authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
|
|
||||||
SecurityContextHolder.getContext().setAuthentication(authToken);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AppPrincipal principal = new AppPrincipal(
|
||||||
|
user.getId(),
|
||||||
|
user.getUsername(),
|
||||||
|
user.getRole(),
|
||||||
|
user.getTokenVersion()
|
||||||
|
);
|
||||||
|
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
|
||||||
|
principal,
|
||||||
|
null,
|
||||||
|
principal.getAuthorities()
|
||||||
|
);
|
||||||
|
authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
|
||||||
|
SecurityContextHolder.getContext().setAuthentication(authToken);
|
||||||
}
|
}
|
||||||
filterChain.doFilter(request, response);
|
filterChain.doFilter(request, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void writeUnauthorized(HttpServletResponse response, String message) throws IOException {
|
||||||
|
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
|
||||||
|
response.setContentType("application/json");
|
||||||
|
response.getWriter().write(
|
||||||
|
"{\"status\":401,\"message\":\"" + message + "\",\"timestamp\":\"" + LocalDateTime.now() + "\"}"
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
package com.petshop.backend.security;
|
package com.petshop.backend.security;
|
||||||
|
|
||||||
|
import com.petshop.backend.entity.User;
|
||||||
import io.jsonwebtoken.Claims;
|
import io.jsonwebtoken.Claims;
|
||||||
import io.jsonwebtoken.Jwts;
|
import io.jsonwebtoken.Jwts;
|
||||||
import io.jsonwebtoken.security.Keys;
|
import io.jsonwebtoken.security.Keys;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.security.core.userdetails.UserDetails;
|
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import javax.crypto.SecretKey;
|
import javax.crypto.SecretKey;
|
||||||
@@ -28,7 +28,20 @@ public class JwtUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String extractUsername(String token) {
|
public String extractUsername(String token) {
|
||||||
return extractClaim(token, Claims::getSubject);
|
return extractAllClaims(token).get("username", String.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long extractUserId(String token) {
|
||||||
|
return Long.parseLong(extractClaim(token, Claims::getSubject));
|
||||||
|
}
|
||||||
|
|
||||||
|
public String extractRole(String token) {
|
||||||
|
return extractAllClaims(token).get("role", String.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer extractTokenVersion(String token) {
|
||||||
|
Number tokenVersion = extractAllClaims(token).get("tokenVersion", Number.class);
|
||||||
|
return tokenVersion == null ? null : tokenVersion.intValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Date extractExpiration(String token) {
|
public Date extractExpiration(String token) {
|
||||||
@@ -52,9 +65,12 @@ public class JwtUtil {
|
|||||||
return extractExpiration(token).before(new Date());
|
return extractExpiration(token).before(new Date());
|
||||||
}
|
}
|
||||||
|
|
||||||
public String generateToken(UserDetails userDetails) {
|
public String generateToken(User user) {
|
||||||
Map<String, Object> claims = new HashMap<>();
|
Map<String, Object> claims = new HashMap<>();
|
||||||
return createToken(claims, userDetails.getUsername());
|
claims.put("username", user.getUsername());
|
||||||
|
claims.put("role", user.getRole().name());
|
||||||
|
claims.put("tokenVersion", user.getTokenVersion());
|
||||||
|
return createToken(claims, user.getId().toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
private String createToken(Map<String, Object> claims, String subject) {
|
private String createToken(Map<String, Object> claims, String subject) {
|
||||||
@@ -67,8 +83,13 @@ public class JwtUtil {
|
|||||||
.compact();
|
.compact();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Boolean validateToken(String token, UserDetails userDetails) {
|
public Boolean validateToken(String token, User user) {
|
||||||
final String username = extractUsername(token);
|
Long userId = extractUserId(token);
|
||||||
return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
|
String role = extractRole(token);
|
||||||
|
Integer tokenVersion = extractTokenVersion(token);
|
||||||
|
return user.getId().equals(userId)
|
||||||
|
&& user.getRole().name().equals(role)
|
||||||
|
&& user.getTokenVersion().equals(tokenVersion)
|
||||||
|
&& !isTokenExpired(token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,129 @@
|
|||||||
|
package com.petshop.backend.security;
|
||||||
|
|
||||||
|
import com.petshop.backend.entity.User;
|
||||||
|
import com.petshop.backend.repository.UserRepository;
|
||||||
|
import jakarta.servlet.FilterChain;
|
||||||
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.mock.web.MockHttpServletRequest;
|
||||||
|
import org.springframework.mock.web.MockHttpServletResponse;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.test.util.ReflectionTestUtils;
|
||||||
|
|
||||||
|
import java.lang.reflect.Proxy;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
class JwtAuthenticationFilterTest {
|
||||||
|
|
||||||
|
private JwtUtil jwtUtil;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
jwtUtil = new JwtUtil();
|
||||||
|
ReflectionTestUtils.setField(jwtUtil, "secret", "change_me_please_make_this_at_least_32_characters_long_for_security");
|
||||||
|
ReflectionTestUtils.setField(jwtUtil, "expiration", 86400000L);
|
||||||
|
SecurityContextHolder.clearContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
void tearDown() {
|
||||||
|
SecurityContextHolder.clearContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void validTokenBuildsAppPrincipalAuthentication() throws Exception {
|
||||||
|
User user = buildUser();
|
||||||
|
String token = jwtUtil.generateToken(user);
|
||||||
|
AtomicBoolean chainCalled = new AtomicBoolean(false);
|
||||||
|
JwtAuthenticationFilter filter = new JwtAuthenticationFilter(jwtUtil, userRepositoryFor(user));
|
||||||
|
|
||||||
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||||
|
request.addHeader("Authorization", "Bearer " + token);
|
||||||
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
|
FilterChain chain = (req, res) -> chainCalled.set(true);
|
||||||
|
|
||||||
|
filter.doFilter(request, response, chain);
|
||||||
|
|
||||||
|
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
|
||||||
|
assertInstanceOf(AppPrincipal.class, principal);
|
||||||
|
assertEquals("staff-user", ((AppPrincipal) principal).getUsername());
|
||||||
|
assertEquals(User.Role.STAFF, ((AppPrincipal) principal).getRole());
|
||||||
|
assertTrue(chainCalled.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void inactiveUserReturnsUnauthorized() throws Exception {
|
||||||
|
User user = buildUser();
|
||||||
|
user.setActive(false);
|
||||||
|
String token = jwtUtil.generateToken(user);
|
||||||
|
JwtAuthenticationFilter filter = new JwtAuthenticationFilter(jwtUtil, userRepositoryFor(user));
|
||||||
|
|
||||||
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||||
|
request.addHeader("Authorization", "Bearer " + token);
|
||||||
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
|
|
||||||
|
filter.doFilter(request, response, (req, res) -> {
|
||||||
|
});
|
||||||
|
|
||||||
|
assertEquals(401, response.getStatus());
|
||||||
|
assertNull(SecurityContextHolder.getContext().getAuthentication());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void tokenVersionMismatchReturnsUnauthorized() throws Exception {
|
||||||
|
User user = buildUser();
|
||||||
|
String token = jwtUtil.generateToken(user);
|
||||||
|
user.setTokenVersion(4);
|
||||||
|
JwtAuthenticationFilter filter = new JwtAuthenticationFilter(jwtUtil, userRepositoryFor(user));
|
||||||
|
|
||||||
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||||
|
request.addHeader("Authorization", "Bearer " + token);
|
||||||
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
|
|
||||||
|
filter.doFilter(request, response, (req, res) -> {
|
||||||
|
});
|
||||||
|
|
||||||
|
assertEquals(401, response.getStatus());
|
||||||
|
assertNull(SecurityContextHolder.getContext().getAuthentication());
|
||||||
|
}
|
||||||
|
|
||||||
|
private UserRepository userRepositoryFor(User user) {
|
||||||
|
return (UserRepository) Proxy.newProxyInstance(
|
||||||
|
UserRepository.class.getClassLoader(),
|
||||||
|
new Class[]{UserRepository.class},
|
||||||
|
(proxy, method, args) -> {
|
||||||
|
if ("findById".equals(method.getName())) {
|
||||||
|
return user.getId().equals(args[0]) ? Optional.of(user) : Optional.empty();
|
||||||
|
}
|
||||||
|
if ("equals".equals(method.getName())) {
|
||||||
|
return proxy == args[0];
|
||||||
|
}
|
||||||
|
if ("hashCode".equals(method.getName())) {
|
||||||
|
return System.identityHashCode(proxy);
|
||||||
|
}
|
||||||
|
if ("toString".equals(method.getName())) {
|
||||||
|
return "UserRepositoryProxy";
|
||||||
|
}
|
||||||
|
throw new UnsupportedOperationException(method.getName());
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private User buildUser() {
|
||||||
|
User user = new User();
|
||||||
|
user.setId(42L);
|
||||||
|
user.setUsername("staff-user");
|
||||||
|
user.setPassword("encoded");
|
||||||
|
user.setRole(User.Role.STAFF);
|
||||||
|
user.setActive(true);
|
||||||
|
user.setTokenVersion(3);
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
}
|
||||||
59
src/test/java/com/petshop/backend/security/JwtUtilTest.java
Normal file
59
src/test/java/com/petshop/backend/security/JwtUtilTest.java
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
package com.petshop.backend.security;
|
||||||
|
|
||||||
|
import com.petshop.backend.entity.User;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.test.util.ReflectionTestUtils;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
class JwtUtilTest {
|
||||||
|
|
||||||
|
private JwtUtil jwtUtil;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
jwtUtil = new JwtUtil();
|
||||||
|
ReflectionTestUtils.setField(jwtUtil, "secret", "change_me_please_make_this_at_least_32_characters_long_for_security");
|
||||||
|
ReflectionTestUtils.setField(jwtUtil, "expiration", 86400000L);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void generatedTokenContainsIdentityClaims() {
|
||||||
|
User user = buildUser();
|
||||||
|
|
||||||
|
String token = jwtUtil.generateToken(user);
|
||||||
|
|
||||||
|
assertEquals(42L, jwtUtil.extractUserId(token));
|
||||||
|
assertEquals("staff-user", jwtUtil.extractUsername(token));
|
||||||
|
assertEquals("STAFF", jwtUtil.extractRole(token));
|
||||||
|
assertEquals(7, jwtUtil.extractTokenVersion(token));
|
||||||
|
assertTrue(jwtUtil.validateToken(token, user));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void validateTokenRejectsChangedRoleOrTokenVersion() {
|
||||||
|
User user = buildUser();
|
||||||
|
String token = jwtUtil.generateToken(user);
|
||||||
|
|
||||||
|
user.setRole(User.Role.ADMIN);
|
||||||
|
assertFalse(jwtUtil.validateToken(token, user));
|
||||||
|
|
||||||
|
user.setRole(User.Role.STAFF);
|
||||||
|
user.setTokenVersion(8);
|
||||||
|
assertFalse(jwtUtil.validateToken(token, user));
|
||||||
|
}
|
||||||
|
|
||||||
|
private User buildUser() {
|
||||||
|
User user = new User();
|
||||||
|
user.setId(42L);
|
||||||
|
user.setUsername("staff-user");
|
||||||
|
user.setPassword("encoded");
|
||||||
|
user.setRole(User.Role.STAFF);
|
||||||
|
user.setActive(true);
|
||||||
|
user.setTokenVersion(7);
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user