Skip to content

Commit c0c3124

Browse files
committed
add: oauth2 jwt
1 parent 8588095 commit c0c3124

File tree

11 files changed

+176
-226
lines changed

11 files changed

+176
-226
lines changed

pom.xml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,15 @@
5151
<scope>provided</scope>
5252
<optional>true</optional>
5353
</dependency>
54+
<dependency>
55+
<groupId>org.springframework.boot</groupId>
56+
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
57+
</dependency>
58+
<dependency>
59+
<groupId>org.springframework.security</groupId>
60+
<artifactId>spring-security-oauth2-authorization-server</artifactId>
61+
<version>1.4.3</version>
62+
</dependency>
5463
<dependency>
5564
<groupId>io.jsonwebtoken</groupId>
5665
<artifactId>jjwt-api</artifactId>
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package com.thisaster.testtask.auth.config;
2+
3+
import com.nimbusds.jose.jwk.RSAKey;
4+
import com.nimbusds.jose.jwk.JWKSet;
5+
import com.nimbusds.jose.jwk.source.ImmutableJWKSet;
6+
import com.nimbusds.jose.jwk.source.JWKSource;
7+
import com.nimbusds.jose.proc.SecurityContext;
8+
import com.thisaster.testtask.auth.config.RsaKeyConfig.RsaKeyPair;
9+
import lombok.RequiredArgsConstructor;
10+
import org.springframework.context.annotation.Bean;
11+
import org.springframework.context.annotation.Configuration;
12+
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
13+
import org.springframework.security.crypto.password.PasswordEncoder;
14+
import org.springframework.security.oauth2.jwt.JwtDecoder;
15+
import org.springframework.security.oauth2.jwt.JwtEncoder;
16+
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
17+
import org.springframework.security.oauth2.jwt.NimbusJwtEncoder;
18+
19+
@Configuration
20+
@RequiredArgsConstructor
21+
public class EncodersConfig {
22+
23+
private final RsaKeyPair rsaKeys;
24+
25+
@Bean
26+
public PasswordEncoder passwordEncoder() {
27+
return new BCryptPasswordEncoder(12);
28+
}
29+
30+
@Bean
31+
public JwtEncoder jwtEncoder() {
32+
RSAKey jwk = new RSAKey.Builder(rsaKeys.publicKey())
33+
.privateKey(rsaKeys.privateKey())
34+
.build();
35+
JWKSource<SecurityContext> jwkSource = new ImmutableJWKSet<>(new JWKSet(jwk));
36+
return new NimbusJwtEncoder(jwkSource);
37+
}
38+
39+
@Bean
40+
public JwtDecoder jwtDecoder() {
41+
return NimbusJwtDecoder.withPublicKey(rsaKeys.publicKey()).build();
42+
}
43+
}

src/main/java/com/thisaster/testtask/auth/config/JwtAuthenticationFilter.java

Lines changed: 0 additions & 68 deletions
This file was deleted.

src/main/java/com/thisaster/testtask/auth/config/PasswordEncoderConfig.java

Lines changed: 0 additions & 15 deletions
This file was deleted.
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package com.thisaster.testtask.auth.config;
2+
3+
import org.springframework.beans.factory.annotation.Value;
4+
import org.springframework.context.annotation.Bean;
5+
import org.springframework.context.annotation.Configuration;
6+
7+
import java.security.KeyFactory;
8+
import java.security.interfaces.RSAPrivateKey;
9+
import java.security.interfaces.RSAPublicKey;
10+
import java.security.spec.PKCS8EncodedKeySpec;
11+
import java.security.spec.X509EncodedKeySpec;
12+
import java.util.Base64;
13+
14+
@Configuration
15+
public class RsaKeyConfig {
16+
17+
@Value("${jwt.privateKey}")
18+
private String privateKeyStr;
19+
20+
@Value("${jwt.publicKey}")
21+
private String publicKeyStr;
22+
23+
@Bean
24+
public RsaKeyPair rsaKeys() {
25+
try {
26+
byte[] privateBytes = Base64.getDecoder().decode(privateKeyStr.replaceAll("\\s+", ""));
27+
byte[] publicBytes = Base64.getDecoder().decode(publicKeyStr.replaceAll("\\s+", ""));
28+
29+
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
30+
31+
RSAPrivateKey privateKey = (RSAPrivateKey) keyFactory.generatePrivate(new PKCS8EncodedKeySpec(privateBytes));
32+
RSAPublicKey publicKey = (RSAPublicKey) keyFactory.generatePublic(new X509EncodedKeySpec(publicBytes));
33+
34+
return new RsaKeyPair(publicKey, privateKey);
35+
} catch (Exception e) {
36+
throw new RuntimeException("Failed to parse RSA keys", e);
37+
}
38+
}
39+
40+
public record RsaKeyPair(RSAPublicKey publicKey, RSAPrivateKey privateKey) {
41+
}
42+
}

src/main/java/com/thisaster/testtask/auth/config/SecurityConfiguration.java

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,55 +4,62 @@
44
import lombok.RequiredArgsConstructor;
55
import org.springframework.context.annotation.Bean;
66
import org.springframework.context.annotation.Configuration;
7-
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
8-
import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;
97
import org.springframework.security.authentication.AuthenticationManager;
108
import org.springframework.security.authentication.AuthenticationProvider;
119
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
10+
import org.springframework.security.authorization.AuthorityAuthorizationManager;
1211
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
1312
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
1413
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
1514
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
1615
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
1716
import org.springframework.security.config.http.SessionCreationPolicy;
1817
import org.springframework.security.crypto.password.PasswordEncoder;
18+
import org.springframework.security.oauth2.jwt.JwtDecoder;
19+
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
20+
import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;
1921
import org.springframework.security.web.SecurityFilterChain;
20-
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
2122

2223
@Configuration
2324
@EnableWebSecurity
2425
@EnableMethodSecurity
2526
@RequiredArgsConstructor
2627
public class SecurityConfiguration {
2728

28-
private final JwtAuthenticationFilter jwtAuthenticationFilter;
2929
private final SecurityUserDetailsService userDetailsService;
3030
private final JwtAuthenticationEntryPoint jwtAuthEntryPoint;
3131
private final PasswordEncoder passwordEncoder;
32+
private final JwtDecoder jwtDecoder;
3233

3334
@Bean
3435
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
3536
http
3637
.csrf(AbstractHttpConfigurer::disable)
3738
.authorizeHttpRequests(request -> request
39+
.requestMatchers("/api/auth/register").access(AuthorityAuthorizationManager.hasRole("SUPERVISOR"))
3840
.requestMatchers("/api", "/swagger-ui/**", "/v1/api-docs/**").permitAll()
3941
.requestMatchers("/api/auth/login").permitAll()
4042
.anyRequest().authenticated())
4143
.exceptionHandling(ex -> ex
4244
.authenticationEntryPoint(jwtAuthEntryPoint))
4345
.sessionManagement(manager -> manager.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
44-
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
46+
.oauth2ResourceServer(rs -> rs
47+
.jwt((jwt) -> jwt.decoder(jwtDecoder).jwtAuthenticationConverter(jwtAuthenticationConverter()))
48+
)
4549
.authenticationProvider(authenticationProvider());
4650

4751
return http.build();
4852
}
4953

5054
@Bean
51-
public RoleHierarchy roleHierarchy() {
52-
return RoleHierarchyImpl.fromHierarchy("""
53-
ROLE_SUPERVISOR > ROLE_ADMIN
54-
ROLE_ADMIN > ROLE_USER
55-
""");
55+
public JwtAuthenticationConverter jwtAuthenticationConverter() {
56+
JwtGrantedAuthoritiesConverter converter = new JwtGrantedAuthoritiesConverter();
57+
converter.setAuthorityPrefix("");
58+
converter.setAuthoritiesClaimName("roles");
59+
60+
JwtAuthenticationConverter jwtConverter = new JwtAuthenticationConverter();
61+
jwtConverter.setJwtGrantedAuthoritiesConverter(converter);
62+
return jwtConverter;
5663
}
5764

5865

src/main/java/com/thisaster/testtask/auth/controller/AuthController.java

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,14 @@
22

33
import com.thisaster.testtask.auth.config.UserPrincipal;
44
import com.thisaster.testtask.auth.service.AuthService;
5-
import com.thisaster.testtask.auth.service.JwtService;
5+
import com.thisaster.testtask.auth.utils.JWTUtils;
66
import com.thisaster.testtask.user.dto.UserDTO;
77
import com.thisaster.testtask.user.entity.User;
88
import com.thisaster.testtask.user.service.UserService;
99
import lombok.RequiredArgsConstructor;
1010
import lombok.extern.slf4j.Slf4j;
1111
import org.springframework.http.HttpStatus;
1212
import org.springframework.http.ResponseEntity;
13-
import org.springframework.security.access.prepost.PreAuthorize;
1413
import org.springframework.security.authentication.AuthenticationManager;
1514
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
1615
import org.springframework.validation.annotation.Validated;
@@ -27,7 +26,7 @@ public class AuthController {
2726

2827
private final AuthService authService;
2928
private final AuthenticationManager authenticationManager;
30-
private final JwtService jwtService;
29+
private final JWTUtils jwtUtils;
3130
private final UserService userService;
3231

3332
@PostMapping("/login")
@@ -38,14 +37,13 @@ public ResponseEntity<Void> login(@RequestBody @Validated UserDTO request) {
3837
authenticationManager.authenticate(authentication);
3938

4039
User user = userService.getUserByLogin(request.getUsername());
41-
String jwt = jwtService.generateToken(new UserPrincipal(user));
40+
String jwt = jwtUtils.generateToken(new UserPrincipal(user));
4241
log.debug("Пользователь {} успешно авторизирован", request.getUsername());
4342
return ResponseEntity.ok()
4443
.header("Authorization", "Bearer " + jwt)
4544
.build();
4645
}
4746

48-
@PreAuthorize("hasRole('SUPERVISOR')")
4947
@PostMapping("/register")
5048
public ResponseEntity<?> register(@RequestBody @Validated UserDTO request) {
5149
authService.registerUser(request);

0 commit comments

Comments
 (0)