From b16d655e8524ce5ff49061ada13dc9e624563282 Mon Sep 17 00:00:00 2001 From: kataus Date: Tue, 14 Mar 2023 14:57:26 +0300 Subject: [PATCH] =?UTF-8?q?=D0=BF=D0=BE=D0=B4=D0=B3=D0=BE=D1=82=D0=BE?= =?UTF-8?q?=D0=B2=D0=BA=D0=B0=20=D0=BF=D1=80=D0=B8=D0=BC=D0=B5=D1=80=D0=BE?= =?UTF-8?q?=D0=B2=20=D0=BA=20=D0=B7=D0=B0=D0=BD=D1=8F=D1=82=D0=B8=D1=8E=20?= =?UTF-8?q?27=20Spring=2022-11?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 2022-11/spring-27/jwt/pom.xml | 78 ++++++++++++++++++ .../java/ru/otus/security/jwt/JwtStarter.java | 12 +++ .../security/jwt/config/SecurityConfig.java | 80 +++++++++++++++++++ .../jwt/controller/HelloController.java | 14 ++++ .../jwt/controller/TokenController.java | 39 +++++++++ .../spring-27/jwt/src/main/resources/app.key | 28 +++++++ .../spring-27/jwt/src/main/resources/app.pub | 9 +++ .../jwt/src/main/resources/application.yml | 10 +++ .../src/main/resources/templates/index.html | 11 +++ .../spring/sso/security/SecurityConfig.java | 2 - 10 files changed, 281 insertions(+), 2 deletions(-) create mode 100644 2022-11/spring-27/jwt/pom.xml create mode 100644 2022-11/spring-27/jwt/src/main/java/ru/otus/security/jwt/JwtStarter.java create mode 100644 2022-11/spring-27/jwt/src/main/java/ru/otus/security/jwt/config/SecurityConfig.java create mode 100644 2022-11/spring-27/jwt/src/main/java/ru/otus/security/jwt/controller/HelloController.java create mode 100644 2022-11/spring-27/jwt/src/main/java/ru/otus/security/jwt/controller/TokenController.java create mode 100644 2022-11/spring-27/jwt/src/main/resources/app.key create mode 100644 2022-11/spring-27/jwt/src/main/resources/app.pub create mode 100644 2022-11/spring-27/jwt/src/main/resources/application.yml create mode 100644 2022-11/spring-27/jwt/src/main/resources/templates/index.html diff --git a/2022-11/spring-27/jwt/pom.xml b/2022-11/spring-27/jwt/pom.xml new file mode 100644 index 00000000..93e46c5d --- /dev/null +++ b/2022-11/spring-27/jwt/pom.xml @@ -0,0 +1,78 @@ + + + 4.0.0 + + ru.otus + spring-framework-27-jwt + 1.0-SNAPSHOT + + + org.springframework.boot + spring-boot-starter-parent + 2.7.8 + + + + 2.6.11 + + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + + org.springframework.boot + spring-boot-starter-security + + + + org.springframework.boot + spring-boot-starter-oauth2-resource-server + + + + org.springdoc + springdoc-openapi-ui + 1.6.9 + + + + org.webjars + jquery + 3.4.1 + + + org.webjars + bootstrap + 4.3.1 + + + org.webjars + webjars-locator-core + + + org.webjars + js-cookie + 2.1.0 + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/2022-11/spring-27/jwt/src/main/java/ru/otus/security/jwt/JwtStarter.java b/2022-11/spring-27/jwt/src/main/java/ru/otus/security/jwt/JwtStarter.java new file mode 100644 index 00000000..79f664b0 --- /dev/null +++ b/2022-11/spring-27/jwt/src/main/java/ru/otus/security/jwt/JwtStarter.java @@ -0,0 +1,12 @@ +package ru.otus.security.jwt; + + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class JwtStarter { + public static void main(String[] args){ + SpringApplication.run( JwtStarter.class, args ); + } +} diff --git a/2022-11/spring-27/jwt/src/main/java/ru/otus/security/jwt/config/SecurityConfig.java b/2022-11/spring-27/jwt/src/main/java/ru/otus/security/jwt/config/SecurityConfig.java new file mode 100644 index 00000000..da34e9d4 --- /dev/null +++ b/2022-11/spring-27/jwt/src/main/java/ru/otus/security/jwt/config/SecurityConfig.java @@ -0,0 +1,80 @@ +package ru.otus.security.jwt.config; + +import com.nimbusds.jose.jwk.JWK; +import com.nimbusds.jose.jwk.JWKSet; +import com.nimbusds.jose.jwk.RSAKey; +import com.nimbusds.jose.jwk.source.ImmutableJWKSet; +import com.nimbusds.jose.jwk.source.JWKSource; +import com.nimbusds.jose.proc.SecurityContext; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.Customizer; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.oauth2.jwt.JwtDecoder; +import org.springframework.security.oauth2.jwt.JwtEncoder; +import org.springframework.security.oauth2.jwt.NimbusJwtDecoder; +import org.springframework.security.oauth2.jwt.NimbusJwtEncoder; +import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationEntryPoint; +import org.springframework.security.oauth2.server.resource.web.access.BearerTokenAccessDeniedHandler; +import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer; +import org.springframework.security.provisioning.InMemoryUserDetailsManager; +import org.springframework.security.web.SecurityFilterChain; + +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; + +@Configuration +public class SecurityConfig { + + @Value("${jwt.public.key}") + RSAPublicKey key; + + @Value("${jwt.private.key}") + RSAPrivateKey priv; + + @Bean + public SecurityFilterChain securityFilterChain( HttpSecurity http ) throws Exception { + + http + .authorizeHttpRequests( ( authorize ) -> authorize + .anyRequest().authenticated() + ) + .csrf( ( csrf ) -> csrf.ignoringAntMatchers( "/token", "/" ) ) + .httpBasic( Customizer.withDefaults() ) + .oauth2ResourceServer( OAuth2ResourceServerConfigurer::jwt ) + .sessionManagement( ( session ) -> session.sessionCreationPolicy( SessionCreationPolicy.STATELESS ) ) + .exceptionHandling( ( exceptions ) -> exceptions + .authenticationEntryPoint( new BearerTokenAuthenticationEntryPoint() ) + .accessDeniedHandler( new BearerTokenAccessDeniedHandler() ) + ); + + return http.build(); + } + + @Bean + UserDetailsService users() { + + return new InMemoryUserDetailsManager( + User.withUsername( "user" ) + .password( "password" ) + .authorities( "app" ) + .build() + ); + } + + @Bean + JwtDecoder jwtDecoder() { + return NimbusJwtDecoder.withPublicKey( this.key ).build(); + } + + @Bean + JwtEncoder jwtEncoder() { + JWK jwk = new RSAKey.Builder( this.key ).privateKey( this.priv ).build(); + JWKSource jwks = new ImmutableJWKSet<>( new JWKSet( jwk ) ); + return new NimbusJwtEncoder( jwks ); + } +} diff --git a/2022-11/spring-27/jwt/src/main/java/ru/otus/security/jwt/controller/HelloController.java b/2022-11/spring-27/jwt/src/main/java/ru/otus/security/jwt/controller/HelloController.java new file mode 100644 index 00000000..4ff42a49 --- /dev/null +++ b/2022-11/spring-27/jwt/src/main/java/ru/otus/security/jwt/controller/HelloController.java @@ -0,0 +1,14 @@ +package ru.otus.security.jwt.controller; + +import org.springframework.security.core.Authentication; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class HelloController { + + @GetMapping("/hello") + public String hello( Authentication authentication ) { + return "Hello, " + authentication.getName() + "!"; + } +} diff --git a/2022-11/spring-27/jwt/src/main/java/ru/otus/security/jwt/controller/TokenController.java b/2022-11/spring-27/jwt/src/main/java/ru/otus/security/jwt/controller/TokenController.java new file mode 100644 index 00000000..c5420966 --- /dev/null +++ b/2022-11/spring-27/jwt/src/main/java/ru/otus/security/jwt/controller/TokenController.java @@ -0,0 +1,39 @@ +package ru.otus.security.jwt.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.oauth2.jwt.JwtClaimsSet; +import org.springframework.security.oauth2.jwt.JwtEncoder; +import org.springframework.security.oauth2.jwt.JwtEncoderParameters; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.time.Instant; +import java.util.stream.Collectors; + +@RestController +public class TokenController { + + @Autowired + JwtEncoder encoder; + + @PostMapping("/token") + public String token( Authentication authentication) { + Instant now = Instant.now(); + long expiry = 36000L; + // @formatter:off + String scope = authentication.getAuthorities().stream() + .map(GrantedAuthority::getAuthority) + .collect( Collectors.joining(" ")); + JwtClaimsSet claims = JwtClaimsSet.builder() + .issuer("self") + .issuedAt(now) + .expiresAt(now.plusSeconds(expiry)) + .subject(authentication.getName()) + .claim("scope", scope) + .build(); + // @formatter:on + return this.encoder.encode( JwtEncoderParameters.from(claims)).getTokenValue(); + } +} diff --git a/2022-11/spring-27/jwt/src/main/resources/app.key b/2022-11/spring-27/jwt/src/main/resources/app.key new file mode 100644 index 00000000..53510079 --- /dev/null +++ b/2022-11/spring-27/jwt/src/main/resources/app.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDcWWomvlNGyQhA +iB0TcN3sP2VuhZ1xNRPxr58lHswC9Cbtdc2hiSbe/sxAvU1i0O8vaXwICdzRZ1JM +g1TohG9zkqqjZDhyw1f1Ic6YR/OhE6NCpqERy97WMFeW6gJd1i5inHj/W19GAbqK +LhSHGHqIjyo0wlBf58t+qFt9h/EFBVE/LAGQBsg/jHUQCxsLoVI2aSELGIw2oSDF +oiljwLaQl0n9khX5ZbiegN3OkqodzCYHwWyu6aVVj8M1W9RIMiKmKr09s/gf31Nc +3WjvjqhFo1rTuurWGgKAxJLL7zlJqAKjGWbIT4P6h/1Kwxjw6X23St3OmhsG6HIn ++jl1++MrAgMBAAECggEBAMf820wop3pyUOwI3aLcaH7YFx5VZMzvqJdNlvpg1jbE +E2Sn66b1zPLNfOIxLcBG8x8r9Ody1Bi2Vsqc0/5o3KKfdgHvnxAB3Z3dPh2WCDek +lCOVClEVoLzziTuuTdGO5/CWJXdWHcVzIjPxmK34eJXioiLaTYqN3XKqKMdpD0ZG +mtNTGvGf+9fQ4i94t0WqIxpMpGt7NM4RHy3+Onggev0zLiDANC23mWrTsUgect/7 +62TYg8g1bKwLAb9wCBT+BiOuCc2wrArRLOJgUkj/F4/gtrR9ima34SvWUyoUaKA0 +bi4YBX9l8oJwFGHbU9uFGEMnH0T/V0KtIB7qetReywkCgYEA9cFyfBIQrYISV/OA ++Z0bo3vh2aL0QgKrSXZ924cLt7itQAHNZ2ya+e3JRlTczi5mnWfjPWZ6eJB/8MlH +Gpn12o/POEkU+XjZZSPe1RWGt5g0S3lWqyx9toCS9ACXcN9tGbaqcFSVI73zVTRA +8J9grR0fbGn7jaTlTX2tnlOTQ60CgYEA5YjYpEq4L8UUMFkuj+BsS3u0oEBnzuHd +I9LEHmN+CMPosvabQu5wkJXLuqo2TxRnAznsA8R3pCLkdPGoWMCiWRAsCn979TdY +QbqO2qvBAD2Q19GtY7lIu6C35/enQWzJUMQE3WW0OvjLzZ0l/9mA2FBRR+3F9A1d +rBdnmv0c3TcCgYEAi2i+ggVZcqPbtgrLOk5WVGo9F1GqUBvlgNn30WWNTx4zIaEk +HSxtyaOLTxtq2odV7Kr3LGiKxwPpn/T+Ief+oIp92YcTn+VfJVGw4Z3BezqbR8lA +Uf/+HF5ZfpMrVXtZD4Igs3I33Duv4sCuqhEvLWTc44pHifVloozNxYfRfU0CgYBN +HXa7a6cJ1Yp829l62QlJKtx6Ymj95oAnQu5Ez2ROiZMqXRO4nucOjGUP55Orac1a +FiGm+mC/skFS0MWgW8evaHGDbWU180wheQ35hW6oKAb7myRHtr4q20ouEtQMdQIF +snV39G1iyqeeAsf7dxWElydXpRi2b68i3BIgzhzebQKBgQCdUQuTsqV9y/JFpu6H +c5TVvhG/ubfBspI5DhQqIGijnVBzFT//UfIYMSKJo75qqBEyP2EJSmCsunWsAFsM +TszuiGTkrKcZy9G0wJqPztZZl2F2+bJgnA6nBEV7g5PA4Af+QSmaIhRwqGDAuROR +47jndeyIaMTNETEmOnms+as17g== +-----END PRIVATE KEY----- \ No newline at end of file diff --git a/2022-11/spring-27/jwt/src/main/resources/app.pub b/2022-11/spring-27/jwt/src/main/resources/app.pub new file mode 100644 index 00000000..0b2ee7b3 --- /dev/null +++ b/2022-11/spring-27/jwt/src/main/resources/app.pub @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3FlqJr5TRskIQIgdE3Dd +7D9lboWdcTUT8a+fJR7MAvQm7XXNoYkm3v7MQL1NYtDvL2l8CAnc0WdSTINU6IRv +c5Kqo2Q4csNX9SHOmEfzoROjQqahEcve1jBXluoCXdYuYpx4/1tfRgG6ii4Uhxh6 +iI8qNMJQX+fLfqhbfYfxBQVRPywBkAbIP4x1EAsbC6FSNmkhCxiMNqEgxaIpY8C2 +kJdJ/ZIV+WW4noDdzpKqHcwmB8FsrumlVY/DNVvUSDIipiq9PbP4H99TXN1o746o +RaNa07rq1hoCgMSSy+85SagCoxlmyE+D+of9SsMY8Ol9t0rdzpobBuhyJ/o5dfvj +KwIDAQAB +-----END PUBLIC KEY----- \ No newline at end of file diff --git a/2022-11/spring-27/jwt/src/main/resources/application.yml b/2022-11/spring-27/jwt/src/main/resources/application.yml new file mode 100644 index 00000000..ad5d3850 --- /dev/null +++ b/2022-11/spring-27/jwt/src/main/resources/application.yml @@ -0,0 +1,10 @@ +logging: + level: + root: INFO + org.springframework.web: INFO + org.springframework.security: INFO +# org.springframework.boot.autoconfigure: DEBUG + +jwt: + private.key: classpath:app.key + public.key: classpath:app.pub \ No newline at end of file diff --git a/2022-11/spring-27/jwt/src/main/resources/templates/index.html b/2022-11/spring-27/jwt/src/main/resources/templates/index.html new file mode 100644 index 00000000..fa9cb9f6 --- /dev/null +++ b/2022-11/spring-27/jwt/src/main/resources/templates/index.html @@ -0,0 +1,11 @@ + + + + + + + +
+/swagger + + diff --git a/2022-11/spring-27/oauth/src/main/java/ru/otus/spring/sso/security/SecurityConfig.java b/2022-11/spring-27/oauth/src/main/java/ru/otus/spring/sso/security/SecurityConfig.java index f310baa1..b357a066 100644 --- a/2022-11/spring-27/oauth/src/main/java/ru/otus/spring/sso/security/SecurityConfig.java +++ b/2022-11/spring-27/oauth/src/main/java/ru/otus/spring/sso/security/SecurityConfig.java @@ -8,7 +8,6 @@ import org.springframework.security.web.authentication.HttpStatusEntryPoint; public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure( HttpSecurity http ) throws Exception { - // @formatter:off http .authorizeRequests( a -> a .antMatchers( "/", "/error", "/webjars/**" ).permitAll() @@ -23,6 +22,5 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { ) .oauth2Login(); - // @formatter:on } }