Différences
Ci-dessous, les différences entre deux révisions de la page.
Les deux révisions précédentes Révision précédente Prochaine révision | Révision précédente | ||
web:framework:spring:oauth2 [2024/04/16 13:09] – [Services et authentification] jcheron | web:framework:spring:oauth2 [2024/04/16 13:58] (Version actuelle) – jcheron | ||
---|---|---|---|
Ligne 167: | Ligne 167: | ||
=== AuthService === | === AuthService === | ||
- | <sxh kotlin> | + | <sxh kotlin;title: AuthService> |
- | import fr.zerp.api.models.User | + | |
- | import fr.zerp.api.repositories.UserRepository | + | |
import org.springframework.beans.factory.annotation.Autowired | import org.springframework.beans.factory.annotation.Autowired | ||
import org.springframework.security.core.Authentication | import org.springframework.security.core.Authentication | ||
Ligne 229: | Ligne 227: | ||
</ | </ | ||
+ | ==== Configuration ==== | ||
+ | |||
+ | <sxh kotlin; | ||
+ | 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 fr.zerp.api.security.JpaUserDetailsService | ||
+ | import fr.zerp.api.security.RsaKeyConfigProperties | ||
+ | import org.slf4j.Logger | ||
+ | import org.slf4j.LoggerFactory | ||
+ | import org.springframework.beans.factory.annotation.Autowired | ||
+ | import org.springframework.context.annotation.Bean | ||
+ | import org.springframework.context.annotation.Configuration | ||
+ | import org.springframework.security.authentication.AuthenticationManager | ||
+ | import org.springframework.security.authentication.ProviderManager | ||
+ | import org.springframework.security.authentication.dao.DaoAuthenticationProvider | ||
+ | import org.springframework.security.config.Customizer | ||
+ | import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity | ||
+ | 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.CorsConfigurer | ||
+ | import org.springframework.security.config.annotation.web.configurers.CsrfConfigurer | ||
+ | import org.springframework.security.config.annotation.web.configurers.SessionManagementConfigurer | ||
+ | import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer | ||
+ | import org.springframework.security.config.http.SessionCreationPolicy | ||
+ | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder | ||
+ | import org.springframework.security.crypto.password.PasswordEncoder | ||
+ | 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.web.SecurityFilterChain | ||
+ | import org.springframework.web.servlet.handler.HandlerMappingIntrospector | ||
+ | |||
+ | |||
+ | @Configuration | ||
+ | @EnableWebSecurity | ||
+ | @EnableMethodSecurity | ||
+ | class SecurityConfig { | ||
+ | |||
+ | @Autowired | ||
+ | lateinit var rsaKeyConfigProperties: | ||
+ | |||
+ | @Autowired | ||
+ | lateinit var userDetailsService: | ||
+ | |||
+ | |||
+ | @Bean | ||
+ | fun authManager(): | ||
+ | val authProvider = DaoAuthenticationProvider() | ||
+ | authProvider.setUserDetailsService(userDetailsService) | ||
+ | authProvider.setPasswordEncoder(passwordEncoder()) | ||
+ | return ProviderManager(authProvider) | ||
+ | } | ||
+ | |||
+ | |||
+ | @Bean | ||
+ | @Throws(Exception:: | ||
+ | fun filterChain(http: | ||
+ | return http | ||
+ | .csrf { csrf: CsrfConfigurer< | ||
+ | csrf.disable() | ||
+ | } | ||
+ | .cors { cors: CorsConfigurer< | ||
+ | .authorizeHttpRequests { auth -> | ||
+ | auth.requestMatchers("/ | ||
+ | auth.requestMatchers("/ | ||
+ | auth.requestMatchers("/ | ||
+ | auth.anyRequest().authenticated() | ||
+ | }.headers { headers -> | ||
+ | headers.frameOptions { it.sameOrigin() } | ||
+ | } | ||
+ | .sessionManagement { s: SessionManagementConfigurer< | ||
+ | s.sessionCreationPolicy( | ||
+ | SessionCreationPolicy.STATELESS | ||
+ | ) | ||
+ | } | ||
+ | .oauth2ResourceServer { oauth2: OAuth2ResourceServerConfigurer< | ||
+ | oauth2.jwt { jwt -> | ||
+ | jwt.decoder( | ||
+ | jwtDecoder() | ||
+ | ) | ||
+ | } | ||
+ | } | ||
+ | .userDetailsService(userDetailsService) | ||
+ | .httpBasic(Customizer.withDefaults()) | ||
+ | .build() | ||
+ | } | ||
+ | |||
+ | @Bean | ||
+ | fun jwtDecoder(): | ||
+ | return NimbusJwtDecoder.withPublicKey(rsaKeyConfigProperties.publicKey).build() | ||
+ | } | ||
+ | |||
+ | @Bean | ||
+ | fun jwtEncoder(): | ||
+ | val jwk: JWK = | ||
+ | RSAKey.Builder(rsaKeyConfigProperties.publicKey).privateKey(rsaKeyConfigProperties.privateKey).build() | ||
+ | |||
+ | val jwks: JWKSource< | ||
+ | return NimbusJwtEncoder(jwks) | ||
+ | } | ||
+ | |||
+ | @Bean | ||
+ | fun passwordEncoder(): | ||
+ | return BCryptPasswordEncoder() | ||
+ | } | ||
+ | |||
+ | companion object { | ||
+ | private val log: Logger = LoggerFactory.getLogger(SecurityConfig:: | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | ==== Authentification ==== | ||
+ | === DTO === | ||
+ | |||
+ | <sxh kotlin> | ||
+ | class AuthDTO { | ||
+ | @JvmRecord | ||
+ | data class LoginRequest(val username: String, val password: String) | ||
+ | |||
+ | @JvmRecord | ||
+ | data class Response(val message: String, val token: String) | ||
+ | } | ||
+ | </ | ||
+ | === Controller === | ||
+ | |||
+ | <sxh kotlin> | ||
+ | @RestController | ||
+ | @RequestMapping("/ | ||
+ | @Validated | ||
+ | class AuthController { | ||
+ | |||
+ | @Autowired | ||
+ | lateinit var authService: | ||
+ | |||
+ | @Autowired | ||
+ | lateinit var authenticationManager: | ||
+ | |||
+ | @PostMapping("/ | ||
+ | @Throws(IllegalAccessException:: | ||
+ | fun login(@RequestBody userLogin: AuthDTO.LoginRequest): | ||
+ | val authentication: | ||
+ | authenticationManager | ||
+ | .authenticate( | ||
+ | UsernamePasswordAuthenticationToken( | ||
+ | userLogin.username, | ||
+ | userLogin.password | ||
+ | ) | ||
+ | ) | ||
+ | SecurityContextHolder.getContext().authentication = authentication | ||
+ | val userDetails = authentication.getPrincipal() as AuthUser | ||
+ | log.info(" | ||
+ | val token = authService.generateToken(authentication) | ||
+ | val response: AuthDTO.Response = AuthDTO.Response(" | ||
+ | return ResponseEntity.ok< | ||
+ | } | ||
+ | |||
+ | companion object { | ||
+ | private val log: Logger = LoggerFactory.getLogger(AuthController:: | ||
+ | } | ||
+ | } | ||
+ | </ |