lab8
This commit is contained in:
@@ -35,6 +35,10 @@ dependencies {
|
||||
implementation 'org.springframework.security:spring-security-config'
|
||||
implementation 'org.springframework.security:spring-security-crypto'
|
||||
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'
|
||||
|
||||
|
||||
@@ -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.Configuration;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.ProviderManager;
|
||||
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.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
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.authentication.LoginUrlAuthenticationEntryPoint;
|
||||
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
|
||||
@@ -22,15 +25,20 @@ public class SecurityConfig {
|
||||
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||
http.csrf(AbstractHttpConfigurer::disable)
|
||||
.authorizeHttpRequests(auth -> auth
|
||||
.requestMatchers("/").permitAll()
|
||||
.requestMatchers("/login").permitAll()
|
||||
.requestMatchers("/register").permitAll()
|
||||
.requestMatchers("/logout").permitAll()
|
||||
// .requestMatchers("/api/**").permitAll()
|
||||
.requestMatchers("/css/**").permitAll()
|
||||
.requestMatchers("/js/**").permitAll()
|
||||
.anyRequest().authenticated()
|
||||
);
|
||||
.requestMatchers("/").permitAll()
|
||||
.requestMatchers("/login/**").permitAll()
|
||||
.requestMatchers("/oauth2/**").permitAll()
|
||||
.requestMatchers("/register").permitAll()
|
||||
.requestMatchers("/logout").permitAll()
|
||||
.requestMatchers("/css/**").permitAll()
|
||||
.requestMatchers("/js/**").permitAll()
|
||||
.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.logout(logout -> logout.logoutSuccessUrl("/").permitAll());
|
||||
@@ -44,6 +52,17 @@ public class SecurityConfig {
|
||||
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
|
||||
public AuthenticationManager authenticationManager(PasswordEncoder passwordEncoder, UserDetailsService userDetailsService) {
|
||||
var provider = new DaoAuthenticationProvider(passwordEncoder);
|
||||
|
||||
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