OAuth 2.0 Resource Server MAC & RSA 토큰 검증 - JwtAuthorizationRsaFilter(RSA)
application.yml
spring:
security:
oauth2:
resourceserver:
jwt:
jws-algorithms: RS256RsaSecuritySigner
public class RsaSecuritySigner extends SecuritySigner{
@Override
public String getJwtToken(UserDetails user, JWK jwk) throws JOSEException {
RSASSASigner jwsSigner = new RSASSASigner(((RSAKey)jwk).toRSAPrivateKey());
return super.getJwtTokenInternal(jwsSigner, user, jwk);
}
}
SecuritySigner를 상속 받아RSA기반 서명 및 토큰을 발행하는 클래스
SecuritySigner
public abstract class SecuritySigner {
protected String getJwtTokenInternal(JWSSigner jwsSigner, UserDetails user, JWK jwk) throws JOSEException {
JWSHeader jwsHeader = new JWSHeader
.Builder((JWSAlgorithm) jwk.getAlgorithm())
.keyID(jwk.getKeyID())
.build();
List<String> authorities = user.getAuthorities().stream().map(auth -> auth.getAuthority()).toList();
JWTClaimsSet jwtClaimsSet = new JWTClaimsSet.Builder()
.subject("user")
.issuer("http://localhost:8081")
.claim("username", user.getUsername())
.claim("authority", authorities)
.expirationTime(new Date(new Date().getTime() + 60 * 1000 * 5)) //5분
.build();
SignedJWT signedJWT = new SignedJWT(jwsHeader, jwtClaimsSet);
signedJWT.sign(jwsSigner);
String jwtToken = signedJWT.serialize();
return jwtToken;
}
public abstract String getJwtToken(UserDetails user, JWK jwk) throws JOSEException;
}SignatureConfig
@Configuration
public class SignatureConfig {
@Bean
public MacSecuritySigner macSecuritySigner() {
return new MacSecuritySigner();
}
//추가
@Bean
public RsaSecuritySigner rsaSecuritySigner() {
return new RsaSecuritySigner();
}
//추가
@Bean
public RSAKey rsaKey() throws JOSEException {
return new RSAKeyGenerator(2048)
.keyID("rsaKey")
.algorithm(JWSAlgorithm.RS256)
.generate();
}
@Bean
public OctetSequenceKey octetSequenceKey() throws JOSEException {
...
}
}JwtAuthorizationFilter
public abstract class JwtAuthorizationFilter extends OncePerRequestFilter {
private final JWSVerifier jwsVerifier;
public JwtAuthorizationFilter(JWSVerifier jwsVerifier) {
this.jwsVerifier = jwsVerifier;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String header = request.getHeader("authorization");
if (header == null || !header.startsWith("Bearer ")) {
filterChain.doFilter(request, response);
return;
}
String token = header.replace("Bearer ", "");
SignedJWT signedJWT;
boolean verify;
try {
signedJWT = SignedJWT.parse(token);
verify = signedJWT.verify(jwsVerifier);
if (verify) {
JWTClaimsSet jwtClaimsSet = signedJWT.getJWTClaimsSet();
String username = jwtClaimsSet.getClaim("username").toString();
List<String> authority = (List) jwtClaimsSet.getClaim("authority");
if (username != null) {
UserDetails user = User.withUsername(username)
.password(UUID.randomUUID().toString().substring(0, 8))
.authorities(authority.get(0))
.build();
Authentication authentication =
new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
SecurityContextHolder.getContextHolderStrategy()
.getContext().setAuthentication(authentication);
}
}
} catch (Exception e) {
e.printStackTrace();
}
filterChain.doFilter(request, response);
}
}
MAC방식과RSA방식을 같이 처리할 수 있게 하기 위해 추상 클래스로 변경
JWSVerifier의 구현체로 RSASSAVerifier와 MACVerifier가 있다.
JwtAuthorizationRsaFilter
public class JwtAuthorizationRsaFilter extends JwtAuthorizationFilter {
public JwtAuthorizationRsaFilter(JWSVerifier jwsVerifier) {
super(jwsVerifier);
}
}SecurityConfig
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {
private final RsaSecuritySigner rsaSecuritySigner;
private final RSAKey rsaKey;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
AuthenticationManagerBuilder builder = http.getSharedObject(AuthenticationManagerBuilder.class);
builder.userDetailsService(userDetailsService());
AuthenticationManager authenticationManager = builder.build();
http
.csrf(AbstractHttpConfigurer::disable)
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(request -> request
.requestMatchers("/").permitAll()
.anyRequest().authenticated())
.authenticationManager(authenticationManager)
.addFilterBefore(jwtAuthenticationFilter(authenticationManager), UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(jwtAuthorizationRsaFilter(), UsernamePasswordAuthenticationFilter.class)
;
return http.build();
}
@Bean
public JwtAuthorizationRsaFilter jwtAuthorizationRsaFilter() throws JOSEException {
return new JwtAuthorizationRsaFilter(new RSASSAVerifier(rsaKey.toRSAPublicKey()));
}
// @Bean
// public JwtAuthorizationMacFilter jwtAuthorizationMacFilter(OctetSequenceKey octetSequenceKey) throws JOSEException {
// return new JwtAuthorizationMacFilter(new MACVerifier(octetSequenceKey.toSecretKey()));
// }
public JwtAuthenticationFilter jwtAuthenticationFilter(AuthenticationManager authenticationManager) throws Exception {
JwtAuthenticationFilter jwtAuthenticationFilter = new JwtAuthenticationFilter(rsaSecuritySigner, rsaKey);
jwtAuthenticationFilter.setAuthenticationManager(authenticationManager);
return jwtAuthenticationFilter;
}
@Bean
public UserDetailsService userDetailsService() {
UserDetails user = User.withUsername("user")
.password("1234")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
@Bean
public PasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
}이전 ↩️ - OAuth 2.0 Resource Server MAC & RSA 토큰 검증 - JwtDecoder 에 의한 검증(MAC)
다음 ↪️ - OAuth 2.0 Resource Server MAC & RSA 토큰 검증 - JwtDecoder 에 의한 검증(RSA)
PreviousOAuth 2.0 Resource Server MAC & RSA 토큰 검증 - JwtAuthorizationMacFilterNextOAuth 2.0 Resource Server MAC & RSA 토큰 검증 - JwtDecoder 에 의한 검증(MAC)
Last updated