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 extractAuthoritiesFromJwt(Jwt jwt) { // 从JWT的claims中提取角色和权限信息 Map claims = jwt.getClaims(); List authorities = new ArrayList<>(); // 提取角色 if (claims.containsKey("roles") && claims.get("roles") instanceof List) { List roles = (List) claims.get("roles"); authorities.addAll(roles.stream() .map(role -> new SimpleGrantedAuthority("ROLE_" + role)) .toList()); } // 提取权限 if (claims.containsKey("permissions") && claims.get("permissions") instanceof List) { List permissions = (List) 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 extractAuthoritiesFromJwt(Jwt jwt) { // // 从JWT的claims中提取角色和权限信息 // Map claims = jwt.getClaims(); // List authorities = new ArrayList<>(); // // // 提取角色 // if (claims.containsKey("roles") && claims.get("roles") instanceof List) { // List roles = (List) claims.get("roles"); // authorities.addAll(roles.stream() // .map(role -> new SimpleGrantedAuthority("ROLE_" + role)) // .toList()); // } // // // 提取权限 // if (claims.containsKey("permissions") && claims.get("permissions") instanceof List) { // List permissions = (List) claims.get("permissions"); // authorities.addAll(permissions.stream() // .map(SimpleGrantedAuthority::new) // .toList()); // } // // return authorities; // } }