107 lines
5.6 KiB
Java
107 lines
5.6 KiB
Java
package com.tanqidi.survey.config;
|
||
|
||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||
import jakarta.servlet.http.HttpServletResponse;
|
||
import org.springframework.context.annotation.Bean;
|
||
import org.springframework.context.annotation.Configuration;
|
||
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.http.SessionCreationPolicy;
|
||
import org.springframework.security.core.GrantedAuthority;
|
||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
|
||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||
import org.springframework.security.web.SecurityFilterChain;
|
||
import org.springframework.security.web.access.AccessDeniedHandler;
|
||
import org.springframework.web.cors.CorsConfiguration;
|
||
|
||
import java.util.*;
|
||
|
||
@Configuration
|
||
@EnableWebSecurity
|
||
@EnableMethodSecurity
|
||
public class OAuth2LoginSecurityConfig {
|
||
|
||
@Bean
|
||
public JwtAuthenticationConverter jwtAuthenticationConverter() {
|
||
JwtAuthenticationConverter converter = new JwtAuthenticationConverter();
|
||
converter.setJwtGrantedAuthoritiesConverter(jwt -> {
|
||
Collection<GrantedAuthority> authorities = new ArrayList<>();
|
||
Object realmAccess = jwt.getClaim("realm_access");
|
||
if (realmAccess instanceof Map<?, ?> map) {
|
||
Object roles = map.get("roles");
|
||
if (roles instanceof Collection<?> roleList) {
|
||
for (Object role : roleList) {
|
||
// System.out.println("ROLE_" + role);
|
||
authorities.add(new SimpleGrantedAuthority("ROLE_" + role));
|
||
}
|
||
}
|
||
}
|
||
return authorities;
|
||
});
|
||
return converter;
|
||
}
|
||
|
||
@Bean
|
||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||
http
|
||
.cors(cors -> cors.configurationSource(request -> {
|
||
CorsConfiguration configuration = new CorsConfiguration();
|
||
// String originsProp = env.getProperty("cors.allowed-origins");
|
||
// 你的前端调试地址,防止出现跨域。可以稍微修改下代码做成从application.properties中读取
|
||
String originsProp = "http://localhost:3000,http://localhost:3002,";
|
||
if (!originsProp.isBlank()) {
|
||
configuration.setAllowedOrigins(Arrays.asList(originsProp.split(",")));
|
||
}
|
||
// 只允许常用的安全方法,满足前端常见请求
|
||
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"));
|
||
// 允许所有请求头,便于前端携带token等自定义头
|
||
configuration.setAllowedHeaders(Arrays.asList("*"));
|
||
// 允许携带cookie和认证信息,适合需要登录态的前后端分离场景
|
||
configuration.setAllowCredentials(true);
|
||
return configuration;
|
||
}))
|
||
.sessionManagement(sessionManagement ->
|
||
sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
|
||
.authorizeHttpRequests(auth -> auth
|
||
// .requestMatchers("/login", "/logout", "/error").permitAll()
|
||
.requestMatchers("/api/admin/**").hasRole("test-service-admin") //
|
||
.requestMatchers("/api/user/**").hasAnyRole("test-service-user", "test-service-admin") //
|
||
.anyRequest().authenticated())
|
||
.oauth2ResourceServer(oauth2 -> oauth2
|
||
.jwt(jwt -> jwt.jwtAuthenticationConverter(jwtAuthenticationConverter())) // 设置JWT权限转换器,支持自定义角色映射
|
||
.authenticationEntryPoint(unauthorizedEntryPoint()) // 指定自定义401响应,token过期/无效也会返回JSON
|
||
)
|
||
.exceptionHandling(handling -> handling
|
||
.accessDeniedHandler(accessDeniedHandler())); // 全局无权限处理
|
||
return http.build();
|
||
}
|
||
|
||
@Bean
|
||
public AuthenticationEntryPoint unauthorizedEntryPoint() {
|
||
return (request, response, authException) -> {
|
||
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
|
||
response.setContentType("application/json;charset=UTF-8");
|
||
Map<String, Object> result = new HashMap<>();
|
||
result.put("code", 401);
|
||
result.put("message", "您的访问令牌无效或已过期,请重新登录后再试。"); // 优雅提示
|
||
result.put("data", null);
|
||
response.getWriter().write(new ObjectMapper().writeValueAsString(result)); // 返回自定义JSON格式
|
||
};
|
||
}
|
||
|
||
@Bean
|
||
public AccessDeniedHandler accessDeniedHandler() {
|
||
return (request, response, accessDeniedException) -> {
|
||
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
|
||
response.setContentType("application/json;charset=UTF-8");
|
||
Map<String, Object> result = new HashMap<>();
|
||
result.put("code", 403);
|
||
result.put("message", "权限不足,您无权访问该资源。"); // 简洁优雅提示
|
||
result.put("data", null);
|
||
response.getWriter().write(new ObjectMapper().writeValueAsString(result));
|
||
};
|
||
}
|
||
}
|