Не работают роли с JWT
такая проблема что сейчас изучаю JWT , делаю с ним авторизацию . На моменте где хочу сделать доступ на определенный url только с ролью , либо авторизированным - не пускает - .requestMatchers("/user/**").hasRole("ADMIN")
. После успешной авторизации должен быть переход по url /user/yes , но ошибка 403,если убрать требование роли то спокойно переходит. В headers токен передается из хранилища браузера. Не совсем понимаю этот момент с ролью. Подскажите что можно тут поправить
@Data
@Entity
@Table(name = "user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
@Column(name = "ROLE")
private String role;
@NotEmpty(message = "Поле не должно быть пустым")
@Size(min = 2, max = 30, message = "Имя пользователя должно иметь не менее 2 и не более 30 символов")
@Email
@Column(name = "EMAIL")
private String email;
@NotEmpty(message = "Поле не должно быть пустым")
// @Size(min = 2, max = 30, message = "Пароль должен быть не менее 2 и не более 30 символов")
@Column(name = "PASSWORD")
private String password;
@NotEmpty(message = "Поле не должно быть пустым")
@Size(min = 2, max = 30, message = "Имя пользователя должно иметь не менее 2 и не более 30 символов")
@Column(name = "USERNAME")
private String userName;
}
@Configuration
public class JwtUtil {
@Value("${jwt_secret}")
private String secret;
public String generateToken(String email, String role) {
Date expirationDate = Date.from(ZonedDateTime.now().plusMinutes(60).toInstant());
return JWT.create()
.withSubject("User details")
.withClaim("email", email)
.withClaim("authorities", role)
.withIssuedAt(new Date())
.withIssuer("registration")
.withExpiresAt(expirationDate)
.sign(Algorithm.HMAC256(secret));
}
public String validateTokenAndRetrieveClaim(String token) throws JWTVerificationException {
JWTVerifier verifier = JWT.require(Algorithm.HMAC256(secret))
.withSubject("User details")
.withIssuer("registration")
.build();
DecodedJWT jwt = verifier.verify(token);
return jwt.getClaim("email").asString();
}
}
@Component
public class JWTFilter extends OncePerRequestFilter {
private final JwtUtil jwtUtil;
private final MyUserDetailsService userDetailsService;
public JWTFilter(JwtUtil jwtUtil, MyUserDetailsService userDetailsService) {
this.jwtUtil = jwtUtil;
this.userDetailsService = userDetailsService;
}
@Override
protected void doFilterInternal(
HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain)
throws ServletException, IOException {
final String authHeader = request.getHeader("Authorization");
System.out.println("Начался doFilterInternal");
System.out.println("authHeader "+ authHeader);
if (authHeader != null && authHeader.startsWith("Bearer ") && !authHeader.isBlank()) {
String jwt = authHeader.substring(7);
if (jwt.isBlank()) {
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Неправильный токен");
} else {
try {
String email = jwtUtil.validateTokenAndRetrieveClaim(jwt);
System.out.println("Сточка до userDetailsService.loadUserByUsername(email)");
UserDetails userDetails = userDetailsService.loadUserByUsername(email);
System.out.println("Сточка после userDetailsService.loadUserByUsername(email)");
System.out.println("userDetails "+ userDetails);
UsernamePasswordAuthenticationToken authToken =
new UsernamePasswordAuthenticationToken(userDetails,userDetails.getPassword(),
userDetails.getAuthorities());
if (SecurityContextHolder.getContext().getAuthentication() == null) {
SecurityContextHolder.getContext().setAuthentication(authToken);
}
}catch (JWTVerificationException exception) {
response.sendError(HttpServletResponse.SC_BAD_REQUEST,"Неправильный токен");
}
}
}System.out.println("request"+request);
System.out.println("response"+response);
filterChain.doFilter(request,response);
}
}
@Configuration
@EnableWebSecurity
Security
public class SecurityConfig{
private final JWTFilter jwtFilter;
public SecurityConfig(JWTFilter jwtFilter) {
this.jwtFilter = jwtFilter;
}
@Bean
public UserDetailsService userDetailsService() {
return new MyUserDetailsService();
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(auth -> auth
.requestMatchers("/user/**").hasRole("ADMIN")
.requestMatchers("/","/static/**","/error","/auth/**").permitAll()
)
.sessionManagement(session -> session.sessionCreationPolicy(STATELESS))
.formLogin(formLogin -> {
formLogin
.loginPage("/auth/login")
.loginProcessingUrl("/process_login")
.usernameParameter("username")
.passwordParameter("password")
.permitAll()
.defaultSuccessUrl("/user/yes", true)
.failureUrl("/auth/login?error=true")
.failureUrl("/auth/login?error");
}
)
.logout((logout) -> logout.logoutUrl("/logout").logoutSuccessUrl("/auth/login"))
.sessionManagement(session -> session.sessionCreationPolicy(STATELESS))
.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class)
.build();
}
@Bean
public AuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(userDetailsService());
provider.setPasswordEncoder(passwordEncoder());
return provider;
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public ServletWebServerFactory servletContainer() {
return new TomcatServletWebServerFactory() {
@Override
protected void postProcessContext(Context context) {
Rfc6265CookieProcessor rfc6265Processor = new Rfc6265CookieProcessor();
rfc6265Processor.setSameSiteCookies("None");
context.setCookieProcessor(rfc6265Processor);
}
};
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
return config.getAuthenticationManager();
}
}
Страница HTML :
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Login</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<div class="wrapper">
<form id="loginForm">
<h1>Login</h1>
<div class="input-box">
<input type="text" id="email" placeholder="Email" required>
</div>
<div class="input-box">
<input type="password" id="password" placeholder="Password" required>
</div>
<br/>
<input type="submit" value="Login" class="btn">
</form>
</div>
<script>
document.getElementById("loginForm").addEventListener("submit", function(event) {
event.preventDefault();
const formData = {
email: document.getElementById("email").value,
password: document.getElementById("password").value
};
fetch('/auth/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(formData)
})
.then(response => response.json())
.then(data => {
if (data.jwt) {
localStorage.setItem('Authorization', data.jwt);
makeAuthorizedRequest('/user/yes', data.jwt);
}
})
.catch(error => console.error('Error:', error));
});
function makeAuthorizedRequest(url, jwt) {
const headers = new Headers();
headers.append('Authorization','Bearer '+jwt);
fetch(url, {
method: 'GET',
headers: headers
})
.then(response => {
// Обработка ответа от сервера
})
.catch(error => console.error('Error:', error));
}
// Добавляем код для отправки запроса с заголовком из localStorage сразу после загрузки страницы
document.addEventListener("DOMContentLoaded", function() {
const jwt = localStorage.getItem('Authorization');
if (jwt) {
makeAuthorizedRequest('/user/yes', jwt);
}
});
</script>
</body>
</html>
И примерно такие данные передаются в токене
{
"sub": "User details",
"email": "mion@mail.ru",
"authorities": "ROLE_ADMIN",
"iat": 1711025019,
"iss": "registration",
"exp": 1711028619
}