lab8
This commit is contained in:
@@ -35,7 +35,11 @@ dependencies {
|
|||||||
implementation 'org.springframework.security:spring-security-config'
|
implementation 'org.springframework.security:spring-security-config'
|
||||||
implementation 'org.springframework.security:spring-security-crypto'
|
implementation 'org.springframework.security:spring-security-crypto'
|
||||||
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6'
|
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6'
|
||||||
|
implementation 'org.springframework.boot:spring-boot-starter-oauth2-authorization-server'
|
||||||
|
implementation 'org.springframework.boot:spring-boot-starter-oauth2-resource-server'
|
||||||
|
implementation 'org.springframework.boot:spring-boot-starter-webflux'
|
||||||
|
implementation 'org.springframework.boot:spring-boot-starter-actuator'
|
||||||
|
|
||||||
developmentOnly 'org.springframework.boot:spring-boot-devtools'
|
developmentOnly 'org.springframework.boot:spring-boot-devtools'
|
||||||
|
|
||||||
implementation 'org.hibernate.orm:hibernate-community-dialects'
|
implementation 'org.hibernate.orm:hibernate-community-dialects'
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package ru.lionarius.isdojplab.config;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.web.reactive.function.client.WebClient;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class ApplicationConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public WebClient webClient() {
|
||||||
|
return WebClient.create();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
package ru.lionarius.isdojplab.config;
|
||||||
|
|
||||||
|
import com.nimbusds.jose.jwk.JWKSet;
|
||||||
|
import com.nimbusds.jose.jwk.RSAKey;
|
||||||
|
import com.nimbusds.jose.jwk.source.JWKSource;
|
||||||
|
import com.nimbusds.jose.proc.SecurityContext;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.core.Ordered;
|
||||||
|
import org.springframework.core.annotation.Order;
|
||||||
|
import org.springframework.security.config.Customizer;
|
||||||
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||||
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
||||||
|
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
|
||||||
|
import org.springframework.security.oauth2.jwt.JwtDecoder;
|
||||||
|
import org.springframework.security.oauth2.server.authorization.client.InMemoryRegisteredClientRepository;
|
||||||
|
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
|
||||||
|
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
|
||||||
|
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
|
||||||
|
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
|
||||||
|
import org.springframework.security.oauth2.server.authorization.settings.ClientSettings;
|
||||||
|
import org.springframework.security.oauth2.server.authorization.settings.TokenSettings;
|
||||||
|
import org.springframework.security.web.SecurityFilterChain;
|
||||||
|
|
||||||
|
import java.security.KeyPair;
|
||||||
|
import java.security.KeyPairGenerator;
|
||||||
|
import java.security.interfaces.RSAPrivateKey;
|
||||||
|
import java.security.interfaces.RSAPublicKey;
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableWebSecurity
|
||||||
|
public class AuthorizationServerConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||||
|
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
|
||||||
|
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
|
||||||
|
http.csrf(AbstractHttpConfigurer::disable)
|
||||||
|
.formLogin(Customizer.withDefaults());
|
||||||
|
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public RegisteredClientRepository registeredClientRepository(PasswordEncoder passwordEncoder) {
|
||||||
|
var secret = passwordEncoder.encode("secret");
|
||||||
|
System.out.println("secret: " + secret);
|
||||||
|
|
||||||
|
RegisteredClient loginClient = RegisteredClient
|
||||||
|
.withId(UUID.randomUUID().toString())
|
||||||
|
.clientId("login-client")
|
||||||
|
.clientSecret(secret)
|
||||||
|
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
|
||||||
|
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
|
||||||
|
.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
|
||||||
|
.redirectUri("http://localhost:8080/login/oauth2/code/login-client")
|
||||||
|
.redirectUri("http://localhost:8080/")
|
||||||
|
.scope("read")
|
||||||
|
.scope("write")
|
||||||
|
.clientSettings(ClientSettings.builder()
|
||||||
|
.requireAuthorizationConsent(true)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.tokenSettings(TokenSettings.builder()
|
||||||
|
.accessTokenTimeToLive(Duration.ofDays(1))
|
||||||
|
.refreshTokenTimeToLive(Duration.ofDays(1))
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
return new InMemoryRegisteredClientRepository(loginClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public AuthorizationServerSettings authorizationServerSettings() {
|
||||||
|
return AuthorizationServerSettings.builder()
|
||||||
|
.issuer("http://localhost:8080")
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public JWKSource<SecurityContext> jwkSource() {
|
||||||
|
RSAKey rsaKey = generateRsa();
|
||||||
|
JWKSet jwkSet = new JWKSet(rsaKey);
|
||||||
|
return (jwkSelect, securityContext) -> jwkSelect.select(jwkSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {
|
||||||
|
return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static RSAKey generateRsa() {
|
||||||
|
KeyPair keyPair = generateRsaKey();
|
||||||
|
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
|
||||||
|
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
|
||||||
|
return new RSAKey.Builder(publicKey)
|
||||||
|
.privateKey(privateKey)
|
||||||
|
.keyID(UUID.randomUUID().toString())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static KeyPair generateRsaKey() {
|
||||||
|
KeyPair keyPair;
|
||||||
|
try {
|
||||||
|
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
|
||||||
|
keyPairGenerator.initialize(2048);
|
||||||
|
keyPair = keyPairGenerator.generateKeyPair();
|
||||||
|
} catch (Exception ex) {
|
||||||
|
throw new IllegalStateException(ex);
|
||||||
|
}
|
||||||
|
return keyPair;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ package ru.lionarius.isdojplab.config;
|
|||||||
|
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.http.HttpMethod;
|
||||||
import org.springframework.security.authentication.AuthenticationManager;
|
import org.springframework.security.authentication.AuthenticationManager;
|
||||||
import org.springframework.security.authentication.ProviderManager;
|
import org.springframework.security.authentication.ProviderManager;
|
||||||
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
|
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
|
||||||
@@ -11,6 +12,8 @@ import org.springframework.security.config.http.SessionCreationPolicy;
|
|||||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
|
||||||
|
import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;
|
||||||
import org.springframework.security.web.SecurityFilterChain;
|
import org.springframework.security.web.SecurityFilterChain;
|
||||||
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
|
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
|
||||||
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
|
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
|
||||||
@@ -22,16 +25,21 @@ public class SecurityConfig {
|
|||||||
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||||
http.csrf(AbstractHttpConfigurer::disable)
|
http.csrf(AbstractHttpConfigurer::disable)
|
||||||
.authorizeHttpRequests(auth -> auth
|
.authorizeHttpRequests(auth -> auth
|
||||||
.requestMatchers("/").permitAll()
|
.requestMatchers("/").permitAll()
|
||||||
.requestMatchers("/login").permitAll()
|
.requestMatchers("/login/**").permitAll()
|
||||||
.requestMatchers("/register").permitAll()
|
.requestMatchers("/oauth2/**").permitAll()
|
||||||
.requestMatchers("/logout").permitAll()
|
.requestMatchers("/register").permitAll()
|
||||||
// .requestMatchers("/api/**").permitAll()
|
.requestMatchers("/logout").permitAll()
|
||||||
.requestMatchers("/css/**").permitAll()
|
.requestMatchers("/css/**").permitAll()
|
||||||
.requestMatchers("/js/**").permitAll()
|
.requestMatchers("/js/**").permitAll()
|
||||||
.anyRequest().authenticated()
|
.requestMatchers(HttpMethod.GET, "/api/**").hasAuthority("scope_read")
|
||||||
);
|
.requestMatchers("/api/**").hasAuthority("scope_write")
|
||||||
|
.anyRequest().authenticated()
|
||||||
|
)
|
||||||
|
.oauth2ResourceServer(oauth2 -> {
|
||||||
|
oauth2.jwt(jwt -> jwt.jwtAuthenticationConverter(jwtAuthenticationConverter()));
|
||||||
|
});
|
||||||
|
|
||||||
http.exceptionHandling(exception -> exception.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login")));
|
http.exceptionHandling(exception -> exception.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login")));
|
||||||
http.logout(logout -> logout.logoutSuccessUrl("/").permitAll());
|
http.logout(logout -> logout.logoutSuccessUrl("/").permitAll());
|
||||||
|
|
||||||
@@ -44,6 +52,17 @@ public class SecurityConfig {
|
|||||||
return http.build();
|
return http.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private JwtAuthenticationConverter jwtAuthenticationConverter() {
|
||||||
|
var converter = new JwtAuthenticationConverter();
|
||||||
|
|
||||||
|
var authoritiesConverter = new JwtGrantedAuthoritiesConverter();
|
||||||
|
authoritiesConverter.setAuthoritiesClaimName("scope");
|
||||||
|
authoritiesConverter.setAuthorityPrefix("scope_");
|
||||||
|
|
||||||
|
converter.setJwtGrantedAuthoritiesConverter(authoritiesConverter);
|
||||||
|
return converter;
|
||||||
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public AuthenticationManager authenticationManager(PasswordEncoder passwordEncoder, UserDetailsService userDetailsService) {
|
public AuthenticationManager authenticationManager(PasswordEncoder passwordEncoder, UserDetailsService userDetailsService) {
|
||||||
var provider = new DaoAuthenticationProvider(passwordEncoder);
|
var provider = new DaoAuthenticationProvider(passwordEncoder);
|
||||||
@@ -51,7 +70,7 @@ public class SecurityConfig {
|
|||||||
|
|
||||||
return new ProviderManager(provider);
|
return new ProviderManager(provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public PasswordEncoder passwordEncoder() {
|
public PasswordEncoder passwordEncoder() {
|
||||||
return new BCryptPasswordEncoder();
|
return new BCryptPasswordEncoder();
|
||||||
|
|||||||
34
src/main/java/ru/lionarius/isdojplab/rest/OAuth2Token.java
Normal file
34
src/main/java/ru/lionarius/isdojplab/rest/OAuth2Token.java
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
package ru.lionarius.isdojplab.rest;
|
||||||
|
|
||||||
|
import org.springframework.util.LinkedMultiValueMap;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import org.springframework.web.reactive.function.client.WebClient;
|
||||||
|
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
public class OAuth2Token {
|
||||||
|
|
||||||
|
private final WebClient webClient;
|
||||||
|
|
||||||
|
public OAuth2Token(WebClient webClient) {
|
||||||
|
this.webClient = webClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping(value = "/login/oauth2/code/login-client", produces = "application/json")
|
||||||
|
public String handle(String code) {
|
||||||
|
var form = new LinkedMultiValueMap<String, String>();
|
||||||
|
form.add("grant_type", "authorization_code");
|
||||||
|
form.add("code", code);
|
||||||
|
form.add("redirect_uri", "http://localhost:8080/login/oauth2/code/login-client");
|
||||||
|
|
||||||
|
return webClient.post()
|
||||||
|
.uri("http://localhost:8080/oauth2/token")
|
||||||
|
.header("Authorization", "Basic " + Base64.getEncoder().encodeToString("login-client:secret".getBytes()))
|
||||||
|
.bodyValue(form)
|
||||||
|
.retrieve()
|
||||||
|
.bodyToMono(String.class)
|
||||||
|
.block();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user