服务调用不鉴权注解
定义注解
java
package org.elsfs.cloud.security.common.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 服务调用不鉴权注解
*
* @author zeng
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface IgnoringAuthentication {}
定义 properties
java
package org.elsfs.cloud.security.common.properties;
import cn.hutool.core.util.ReUtil;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.regex.Pattern;
import lombok.Data;
import org.elsfs.cloud.security.common.annotation.IgnoringAuthentication;
import org.elsfs.cloud.security.common.core.jwt.RsaKeyPair;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.http.HttpMethod;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
/**
* security 参数
*
* @author zeng
*/
@Data
@ConfigurationProperties(prefix = "elsfs.security")
public class ElsfsSecurityProperties implements BeanFactoryAware {
private static final Pattern PATTERN = Pattern.compile("\\{(.*?)\\}");
private BeanFactory beanFactory;
private final List<RequestMatcher> requestMatchers = new ArrayList<>();
private static final String[] DEFAULT_IGNORE_URLS =
new String[] {
"/**.html",
"/**.css",
"/css/**.css",
"/js/**.js",
"/templates/css/**",
"/webjars/**",
"/actuator/**",
"/error",
"/token/**",
"/favicon.ico",
"/img/**",
"/login",
"/error",
// PhonePasswordAuthenticationFilter.DEFAULT_FILTER_PROCESSES_URI
};
private String issuer;
private String consentPage = "/oauth2/consent";
private Set<RsaKeyPair> keyPairs = new HashSet<>();
private Cors cors = new Cors();
private Map<RequestMethod, String[]> ignoreHttpMethod =
Map.of(
RequestMethod.GET,
new String[] {},
RequestMethod.POST,
new String[] {},
RequestMethod.DELETE,
new String[] {},
RequestMethod.PUT,
new String[] {});
/**
* 获取所有忽略认证的RequestMatcher
*
* @return RequestMatcher
*/
public RequestMatcher getRequestMatcher() {
List<RequestMatcher> matcherList = new ArrayList<>();
for (String ignoreUrl : DEFAULT_IGNORE_URLS) {
matcherList.add(AntPathRequestMatcher.antMatcher(HttpMethod.GET, ignoreUrl));
}
Map<RequestMethod, String[]> method = getIgnoreHttpMethod();
for (RequestMethod httpMethod : method.keySet()) {
for (String path : method.get(httpMethod)) {
matcherList.add(new AntPathRequestMatcher(path, httpMethod.name()));
}
}
afterPropertiesSet();
matcherList.addAll(requestMatchers);
return new OrRequestMatcher(matcherList);
}
protected void afterPropertiesSet() {
RequestMappingHandlerMapping mapping =
beanFactory.getBean("requestMappingHandlerMapping", RequestMappingHandlerMapping.class);
Map<RequestMappingInfo, HandlerMethod> map = mapping.getHandlerMethods();
for (RequestMappingInfo info : map.keySet()) {
// 获取方法
HandlerMethod handlerMethod = map.get(info);
IgnoringAuthentication method =
AnnotationUtils.findAnnotation(handlerMethod.getMethod(), IgnoringAuthentication.class);
// 获取请求方式
Set<RequestMethod> methods = info.getMethodsCondition().getMethods();
String methodStr = methods.isEmpty() ? null : methods.iterator().next().name();
Optional.ofNullable(method)
.ifPresent(
ignoringAuthentication ->
Objects.requireNonNull(info.getPathPatternsCondition())
.getPatternValues()
.forEach(
patternValue ->
requestMatchers.add(
new AntPathRequestMatcher(patternValue, methodStr))));
// 获取类上边的注解, 替代path variable 为 *
IgnoringAuthentication controller =
AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), IgnoringAuthentication.class);
Optional.ofNullable(controller)
.ifPresent(
inner ->
Objects.requireNonNull(info.getPathPatternsCondition())
.getPatternValues()
.forEach(
uri ->
requestMatchers.add(
new AntPathRequestMatcher(
ReUtil.replaceAll(uri, PATTERN, "*")))));
}
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
/** Cors */
@Data
public static class Cors {
private boolean enabled = false;
/**
* 跨域请求允许的源 例如:
*
* <ul>
* <li>特定域,例如 {@code https://domain1.com}
* <li>以逗号分隔的特定域列表:{@code https://a1.com,https://a2.com}
* <li>CORS为所有原点定义了特殊值{@code *}
* </ul>
*/
private List<String> origins = List.of(CorsConfiguration.ALL);
/**
* 替代 origins,它支持更灵活的起源模式,除了端口列表之外,主机名中的任何位置都有“*”。示例:
*
* <ul>
* <li>https://*.domain1.com--以domain1.com结尾的域
* <li>https://*.domain1.com:[8080081]--端口8080或端口8081上以domain1.com结尾的域
* <li>https://*.domain1.com:[*]--任何端口上以domain1.com结尾的域,包括默认端口
* <li>*
* </ul>
*/
private List<String> allowedOriginPatterns = List.of(CorsConfiguration.ALL);
/** 允许跨域的请求头 */
private List<String> allowedHeaders = List.of(CorsConfiguration.ALL);
/**
* 允许跨域的请求方式
*
* @see org.springframework.http.HttpMethod
*/
private List<String> allowedMethods = List.of(CorsConfiguration.ALL);
}
}
实现
java
@Configuration
@EnableConfigurationProperties(ElsfsSecurityProperties.class)
class config{
private ElsfsSecurityProperties getElsfsSecurityProperties(HttpSecurity httpSecurity) {
return getApplicationContext(httpSecurity).getBean(ElsfsSecurityProperties.class);
}
/**
* 添加认证的请求
*
* @param http http
* @throws Exception 配置异常
*/
@Bean
@Order(2)
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
ElsfsSecurityProperties properties = getElsfsSecurityProperties(http);
http.authorizeHttpRequests(
(authorize) -> {
authorize.requestMatchers(CorsUtils::isPreFlightRequest).permitAll();
authorize.requestMatchers(properties.getRequestMatcher()).permitAll();
authorize.anyRequest().authenticated();
});
return http.build();
}
}
使用
java
/**
* text
*
* @author zeng
*/
@RestController
public class TestController {
/**
* test
*
* @return r
*/
@GetMapping("/test")
@IgnoringAuthentication
public R<?> test() {
return R.success();
}
}