190 lines
7.7 KiB
Java
190 lines
7.7 KiB
Java
|
package com.example.demo.config;
|
||
|
|
||
|
import com.example.demo.exception.CustomAuthenticationEntryPoint;
|
||
|
import lombok.Getter;
|
||
|
import lombok.Setter;
|
||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||
|
import org.springframework.beans.factory.annotation.Value;
|
||
|
import org.springframework.context.annotation.Bean;
|
||
|
import org.springframework.context.annotation.Configuration;
|
||
|
import org.springframework.security.authentication.AuthenticationServiceException;
|
||
|
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.config.http.SessionCreationPolicy;
|
||
|
import org.springframework.security.core.GrantedAuthority;
|
||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||
|
import org.springframework.security.oauth2.jwt.Jwt;
|
||
|
import org.springframework.security.oauth2.jwt.JwtDecoder;
|
||
|
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
|
||
|
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
|
||
|
import org.springframework.security.web.SecurityFilterChain;
|
||
|
|
||
|
import java.util.*;
|
||
|
|
||
|
@Configuration
|
||
|
@EnableWebSecurity
|
||
|
public class SecurityConfig {
|
||
|
@Getter
|
||
|
@Setter
|
||
|
private CustomAuthenticationEntryPoint customAuthenticationEntryPoint;
|
||
|
|
||
|
@Autowired
|
||
|
public SecurityConfig(CustomAuthenticationEntryPoint customAuthenticationEntryPoint) {
|
||
|
this.customAuthenticationEntryPoint = customAuthenticationEntryPoint;
|
||
|
}
|
||
|
|
||
|
|
||
|
private static final String[] WHITE_LIST = {"/swagger-ui/**", "/v3/api-docs/**", "/swagger/**"};
|
||
|
|
||
|
// @Value("${spring.security.oauth2.resourceserver.jwt.issuer-uri}")
|
||
|
// String issuerUri;
|
||
|
|
||
|
@Value("${spring.security.oauth2.resourceserver.jwt.jwk-set-uri}")
|
||
|
private String jwkSetUri;
|
||
|
|
||
|
String jwtTokenType = "id_token";
|
||
|
|
||
|
@Bean
|
||
|
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||
|
http.exceptionHandling(
|
||
|
exception -> exception.authenticationEntryPoint(customAuthenticationEntryPoint))
|
||
|
.authorizeHttpRequests(authorizeRequests ->
|
||
|
authorizeRequests.requestMatchers(WHITE_LIST).permitAll().anyRequest().authenticated()
|
||
|
)
|
||
|
.sessionManagement(sessionManagement ->
|
||
|
sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
|
||
|
)
|
||
|
.csrf(AbstractHttpConfigurer::disable)
|
||
|
.cors(AbstractHttpConfigurer::disable)
|
||
|
.oauth2ResourceServer(oauth2ResourceServerConfigurer ->
|
||
|
oauth2ResourceServerConfigurer.jwt(jwtConfigurer -> {
|
||
|
jwtConfigurer.decoder(customJwtDecoder());
|
||
|
jwtConfigurer.jwtAuthenticationConverter(jwtAuthenticationConverter());
|
||
|
})
|
||
|
);
|
||
|
;
|
||
|
|
||
|
return http.build();
|
||
|
}
|
||
|
|
||
|
@Bean
|
||
|
public JwtDecoder customJwtDecoder() {
|
||
|
// 使用NimbusJwtDecoder从JWKS URL读取密钥
|
||
|
return NimbusJwtDecoder.withJwkSetUri(jwkSetUri).jwtProcessorCustomizer(jwtProcessor -> {
|
||
|
jwtProcessor.setJWETypeVerifier((header, context) -> {
|
||
|
if (!header.getType().equals(jwtTokenType)) {
|
||
|
throw new AuthenticationServiceException("Invalid token type: " + header.getType());
|
||
|
}
|
||
|
});
|
||
|
|
||
|
jwtProcessor.setJWSTypeVerifier((joseObjectType, securityContext) -> {
|
||
|
if (!joseObjectType.getType().equals(jwtTokenType)) {
|
||
|
throw new AuthenticationServiceException("Invalid token type: " + joseObjectType);
|
||
|
}
|
||
|
});
|
||
|
}).build();
|
||
|
}
|
||
|
|
||
|
@Bean
|
||
|
public JwtAuthenticationConverter jwtAuthenticationConverter() {
|
||
|
JwtAuthenticationConverter converter = new JwtAuthenticationConverter();
|
||
|
// 提取权限
|
||
|
converter.setJwtGrantedAuthoritiesConverter(this::extractAuthoritiesFromJwt);
|
||
|
return converter;
|
||
|
}
|
||
|
|
||
|
private Collection<GrantedAuthority> extractAuthoritiesFromJwt(Jwt jwt) {
|
||
|
// 从JWT的claims中提取角色和权限信息
|
||
|
Map<String, Object> claims = jwt.getClaims();
|
||
|
List<GrantedAuthority> authorities = new ArrayList<>();
|
||
|
|
||
|
// 提取角色
|
||
|
if (claims.containsKey("roles") && claims.get("roles") instanceof List) {
|
||
|
List<String> roles = (List<String>) claims.get("roles");
|
||
|
authorities.addAll(roles.stream()
|
||
|
.map(role -> new SimpleGrantedAuthority("ROLE_" + role))
|
||
|
.toList());
|
||
|
}
|
||
|
|
||
|
// 提取权限
|
||
|
if (claims.containsKey("permissions") && claims.get("permissions") instanceof List) {
|
||
|
List<String> permissions = (List<String>) claims.get("permissions");
|
||
|
authorities.addAll(permissions.stream()
|
||
|
.map(SimpleGrantedAuthority::new)
|
||
|
.toList());
|
||
|
}
|
||
|
|
||
|
return authorities;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// @Bean
|
||
|
// public JwtDecoder customJwtDecoder() {
|
||
|
// return token -> {
|
||
|
// try {
|
||
|
// // 使用Nimbus库解析JWT
|
||
|
// JWT jwt = JWTParser.parse(token);
|
||
|
// String typ = jwt.getHeader().getType().toString();
|
||
|
// // 检查JWT的typ是否为id_token
|
||
|
// if (!typ.equals(jwtTokenType)) {
|
||
|
// throw new AuthenticationServiceException("Invalid token type: " + typ);
|
||
|
// }
|
||
|
//
|
||
|
// JWTClaimsSet claims = jwt.getJWTClaimsSet();
|
||
|
//
|
||
|
//// Date exp = claims.getExpirationTime();
|
||
|
//// Date now = new Date();
|
||
|
// // 检测是否过期
|
||
|
// if (claims.getExpirationTime().before(new Date())) {
|
||
|
// throw new AuthenticationServiceException("Token has expired");
|
||
|
// }
|
||
|
//
|
||
|
// // 将JWT转换为Spring Security的Jwt对象
|
||
|
// return new Jwt(token, jwt.getJWTClaimsSet().getIssueTime().toInstant(), jwt.getJWTClaimsSet().getExpirationTime().toInstant(), jwt.getHeader().toJSONObject(), jwt.getJWTClaimsSet().toJSONObject());
|
||
|
// } catch (AuthenticationServiceException | ParseException e) {
|
||
|
// // 401 了
|
||
|
// throw new AuthenticationServiceException(e.getMessage());
|
||
|
// }
|
||
|
// };
|
||
|
//
|
||
|
//
|
||
|
// }
|
||
|
//
|
||
|
// @Bean
|
||
|
// public JwtAuthenticationConverter jwtAuthenticationConverter() {
|
||
|
// JwtAuthenticationConverter converter = new JwtAuthenticationConverter();
|
||
|
// // 提取权限
|
||
|
// converter.setJwtGrantedAuthoritiesConverter(this::extractAuthoritiesFromJwt);
|
||
|
//
|
||
|
// return converter;
|
||
|
// }
|
||
|
//
|
||
|
//
|
||
|
// private Collection<GrantedAuthority> extractAuthoritiesFromJwt(Jwt jwt) {
|
||
|
// // 从JWT的claims中提取角色和权限信息
|
||
|
// Map<String, Object> claims = jwt.getClaims();
|
||
|
// List<GrantedAuthority> authorities = new ArrayList<>();
|
||
|
//
|
||
|
// // 提取角色
|
||
|
// if (claims.containsKey("roles") && claims.get("roles") instanceof List) {
|
||
|
// List<String> roles = (List<String>) claims.get("roles");
|
||
|
// authorities.addAll(roles.stream()
|
||
|
// .map(role -> new SimpleGrantedAuthority("ROLE_" + role))
|
||
|
// .toList());
|
||
|
// }
|
||
|
//
|
||
|
// // 提取权限
|
||
|
// if (claims.containsKey("permissions") && claims.get("permissions") instanceof List) {
|
||
|
// List<String> permissions = (List<String>) claims.get("permissions");
|
||
|
// authorities.addAll(permissions.stream()
|
||
|
// .map(SimpleGrantedAuthority::new)
|
||
|
// .toList());
|
||
|
// }
|
||
|
//
|
||
|
// return authorities;
|
||
|
// }
|
||
|
|
||
|
}
|