PIG框架学习2——资源服务器的配置详解
一、前言
1、pig资源服务器的配置
Spring Security oauth2相关的依赖是在pigx-common-security模块中引入的,其他模块需要进行token鉴权的,需要在微服务中引入pigx-common-security模块的依赖,从而间接引入相关的Spring security oauth2依赖。
其最简单的一个目的,是对资源进行保护,对访问资源时携带的token进行鉴权。
微服务,开启资源服务器配置步骤:
①引入相关的依赖
<!--安全模块-->
<dependency><groupId>com.pig4cloud</groupId><artifactId>pig-common-security</artifactId><version>laster.version</version>
</dependency>
②main方法开启@EnablePigResourceServer
pig4cloud对Spring Security OAuth2的资源服务器配置进行了封装,只需要一个注解即可完成相关的操作。
二、EnablePigxResourceServer解析
1、EnablePigxResourceServer的源码
/*
用于指示编译器将被注解的元素的注释信息包含在生成的文档中
使用该自定义注解的地方会在生成的文档中显示该注解的信息和说明
*/
@Documented
/*
用于指示一个自定义注解是否具有继承性
当使用@Inherited注解某个自定义注解时,如果一个类或接口使用了该被注解的自定义注解,那么其子类或实现类也会自动被应用该注解
*/
@Inherited
/*
用于限定自定义注解可以应用的目标元素类型
TYPE 类或接口; FIELD 字段(成员变量);
METHOD 方法;PARAMETER 方法参数;
CONSTRUCTOR 构造函数;LOCAL_VARIABLE 局部变量;
ANNOTATION_TYPE 注解类型;PACKAGE 包;
TYPE_PARAMETER 类型参数;TYPE_USE 类型使用;
*/
@Target({ ElementType.TYPE })
/*
指定自定义注解的保留策略
SOURCE: 自定义注解仅在源代码中保留,编译后不包含
CLASS: 自定义注解在编译后的字节码文件中保留,但不会被加载到虚拟机中
RUNTIME: 自定义注解在运行时保留
*/
@Retention(RetentionPolicy.RUNTIME)
/*
@Import注解主要用于将其他配置类导入到当前的配置类中,以实现配置的组合和复用,而不是用于创建Bean对象
*/
@Import({ PigxResourceServerAutoConfiguration.class, PigxResourceServerConfiguration.class,PigxFeignClientConfiguration.class })
public @interface EnablePigxResourceServer {}
2、PigxResourceServerAutoConfiguration.class源码:
/*
用于自动生成一个包含所有非final和非null字段的构造函数
*/
@RequiredArgsConstructor
/*
只要在加载PigxResourceServerAutoConfiguration时
才会去加载对应的属性配置类:PermitAllUrlProperties
注意: 通过该注解引入的配置@Import({EnableConfigurationPropertiesRegistrar.class}),
会将被@ConfigurationProperties 注解标记的目标类PermitAllUrlProperties注册为一个bean对象
目的:减少spring管控在资源数量 详情见2.1
*/
@EnableConfigurationProperties(PermitAllUrlProperties.class)
public class PigxResourceServerAutoConfiguration {/*** 鉴权具体的实现逻辑 详情见2.2* @return (#pms.xxx)*/@Bean("pms")public PermissionService permissionService() {return new PermissionService();}/*** 请求令牌的抽取逻辑 详情见2.3* @param urlProperties 对外暴露的接口列表* @return BearerTokenExtractor*/@Beanpublic PigxBearerTokenExtractor pigBearerTokenExtractor(PermitAllUrlProperties urlProperties) {return new PigxBearerTokenExtractor(urlProperties);}/*** 资源服务器异常处理 详情见2.4* @param objectMapper jackson 输出对象* @param securityMessageSource 自定义国际化处理器* @return ResourceAuthExceptionEntryPoint*/@Beanpublic ResourceAuthExceptionEntryPoint resourceAuthExceptionEntryPoint(ObjectMapper objectMapper,MessageSource securityMessageSource) {return new ResourceAuthExceptionEntryPoint(objectMapper, securityMessageSource);}/*** 资源服务器toke内省处理器 详情见2.5* @param authorizationService token 存储实现* @return TokenIntrospector*/@Beanpublic OpaqueTokenIntrospector opaqueTokenIntrospector(OAuth2AuthorizationService authorizationService) {return new PigxCustomOpaqueTokenIntrospector(authorizationService);}}
2.1、属性配置类:PermitAllUrlProperties
①将默认的忽略地址加入ignoreUrls列表
②将配置文件中配置的地址加入到ignoreUrls列表
③通过请求映射器获得所有的请求控制器,将添加@inner注解的请求地址加入到ignoreUrls列表
//自动添加日志记录器(Logger)的字段,实现了简化日志记录的功能
@Slf4j
/*
@ConfigurationProperties将配置文件中以指定前缀开头的属性值映射到一个Java类中,
以方便统一管理和使用
*/
@ConfigurationProperties(prefix = "security.oauth2.client")
/*
InitializingBean:
在Bean声明周期中的初始化操作,InitializingBean接口中有一个afterPropertiesSet()方法,
其执行时机早于init-method配置的方法,其是在所有的bean实例化完成并完成依赖注入后执行的,
自动调用实现了InitializingBean接口的bean的afterPropertiesSet()方法,即在bean实例化后和依赖注入后执行的回调方法
注意:implements InitializingBean接口并不是在所有类中都能生效的,它只适用于Spring容器中的bean对象
*/
public class PermitAllUrlProperties implements InitializingBean {private static final Pattern PATTERN = Pattern.compile("\\{(.*?)\\}");private static final String[] DEFAULT_IGNORE_URLS = new String[] { "/actuator/**", "/error", "/v3/api-docs" };//在配置文件中指定的需要忽略的url@Getter@Setterprivate List<String> ignoreUrls = new ArrayList<>();//在Bean属性设置后执行该方法@Overridepublic void afterPropertiesSet() {//忽略url的列表中先加入默认忽略的urlignoreUrls.addAll(Arrays.asList(DEFAULT_IGNORE_URLS));/*RequestMappingHandlerMapping 是 Spring MVC 中的一个重要组件,它负责将请求映射到具体的处理方法(handler method)在 Spring MVC 的处理流程中,RequestMappingHandlerMapping 会根据请求的 URL 和请求方式(GET、POST 等)来确定需要调用哪个处理方法,从而完成请求的处理过程*/RequestMappingHandlerMapping mapping = SpringContextHolder.getBean("requestMappingHandlerMapping");//RequestMappingInfo:请求映射信息,包括请求路径、请求方式等//HandlerMethod:获得所有处理方法的具体信息,包括所属的类、方法名、参数列表等存放到Map<RequestMappingInfo, HandlerMethod> map = mapping.getHandlerMethods();//处理@Inner注解的方法和类,将其添加到ignoreUrls列表中map.keySet().forEach(info -> {//获取对应的映射处理方法HandlerMethod handlerMethod = map.get(info);// 获取方法上边的注解 替代path variable 为 *//通过AnnotationUtils获取当前映射处理方法上的Inner注解,赋值给method,如果没有inner注解,method的值为nullInner method = AnnotationUtils.findAnnotation(handlerMethod.getMethod(), Inner.class);//如果method不为空(当前方法添加Inner注解)将映射的url通过正则表达式解析后加入到ignoreurls列表中//正则表达式主要是对路径上的参数进行处理,匹配{}中的内容,然后替换为*Optional.ofNullable(method).ifPresent(inner -> Objects.requireNonNull(info.getPathPatternsCondition()).getPatternValues().forEach(url -> ignoreUrls.add(ReUtil.replaceAll(url, PATTERN, "*"))));// 获取类上边的注解, 替代path variable 为 *//同理方法Inner controller = AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), Inner.class);Optional.ofNullable(controller).ifPresent(inner -> Objects.requireNonNull(info.getPathPatternsCondition()).getPatternValues().forEach(url -> ignoreUrls.add(ReUtil.replaceAll(url, PATTERN, "*"))));});}}
Map<RequestMappingInfo, HandlerMethod>内容如下所示:

2.2、接口权限判断工具:PermissionService
/*** 鉴权具体的实现逻辑* @return (#pms.xxx)*/
@Bean("pms")
public PermissionService permissionService() {return new PermissionService();
}
具体解析
public class PermissionService {/*** 判断接口是否有任意xxx,xxx权限* @param permissions 权限* @return {boolean}*///String... 可变参数,允许将任意数量的String参数打包成一个数组//可以将一个 ArrayList 作为参数传递给可变参数 String... permissions//eg:hasPermission("param1", "param2")、hasPermission(Arrays.asList("param1", "param2"))public boolean hasPermission(String... permissions) {//入参为空,返回falseif (ArrayUtil.isEmpty(permissions)) {return false;}//从用户的安全上下文信息获取权限信息Authentication authentication = SecurityContextHolder.getContext().getAuthentication();//用户权限信息为null,返回falseif (authentication == null) {return false;}//获得权限信息赋值给authoritiesCollection<? extends GrantedAuthority> authorities = authentication.getAuthorities();//权限是否匹配return authorities.stream().map(GrantedAuthority::getAuthority).filter(StringUtils::hasText).anyMatch(x -> PatternMatchUtils.simpleMatch(permissions, x));}}
具体使用方式:
/**
* 更新角色菜单
*
* @param roleVo 角色对象
* @return success、false
*/
@SysLog("更新角色菜单")
@PutMapping("/menu")
@PreAuthorize("@pms.hasPermission('sys_role_perm')")
public R saveRoleMenus(@RequestBody RoleVO roleVo) {return R.ok(sysRoleService.updateRoleMenus(roleVo));
}
使用的Spring Security的@PreAuthorize注解,用于指定方法执行前需要满足的权限要求,它通常用于控制访问某些受保护资源时的权限控制。
在 @PreAuthorize 中,可以指定一个 SpEL 表达式作为权限要求,如@PreAuthorize("@pms.hasPermission('sys_role_perm')")
“@pms”是 SpEL 中使用的 Spring EL Bean 引用语法,表示引用名为 pms 的 Bean。hasPermission 是 pms Bean 中定义的一个方法,用于检查当前用户是否拥有指定的权限。
因此,上述注解的作用是,当执行该方法时,应该检查当前用户是否具有 sys_role_perm 权限。如果当前用户不具备该权限,方法将被拒绝执行,抛出 AccessDeniedException 异常。
注意:使用的Spring Security的@PreAuthorize注解,需要配置全局的方法级安全性设置,启用 Spring Security 的方法级安全性(Method Security)意味着你可以在方法级别上对访问权限进行控制。通过使用 @PreAuthorize、@PostAuthorize、@Secured 等注解,你可以在方法执行前或执行后对用户的权限进行验证。
在yml中配置:
spring:security:enabled: truemethod:security:enabled: true
在xml配置文件中配置:
<beans:beans xmlns="http://www.springframework.org/schema/security"xmlns:beans="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/securityhttp://www.springframework.org/schema/security/spring-security.xsd"><global-method-security pre-post-annotations="enabled" secured-annotations="enabled" /><!-- 其他配置 -->
</beans:beans>
在pig中是直接通过注解@EnableMethodSecurity开启的
@Slf4j
@EnableWebSecurity
@EnableMethodSecurity
@RequiredArgsConstructor
public class PigxResourceServerConfiguration {……
}
2.3、请求令牌的抽取逻辑:PigxBearerTokenExtractor
/*** 请求令牌的抽取逻辑* @param urlProperties 对外暴露的接口列表* @return BearerTokenExtractor*/
@Bean
public PigxBearerTokenExtractor pigBearerTokenExtractor(PermitAllUrlProperties urlProperties) {return new PigxBearerTokenExtractor(urlProperties);
}
即获取请求中的token的相关逻辑
//BearerTokenResolver:是Spring Security中的一个接口,用于解析Bearer Token,并将其返回
//该接口定义了一个方法 resolve(HttpServletRequest request),用于从请求中提取出 Bearer Token,需要在实现类中重写
//这里pigx自定义了一个类PigxBearerTokenExtractor作为BearerTokenResolver的实现类,用于解析Bearer Token
public class PigxBearerTokenExtractor implements BearerTokenResolver
{//定义处理Bearer Token 的正则表达式模式private static final Pattern authorizationPattern = Pattern.compile("^Bearer (?<token>[a-zA-Z0-9-:._~+/]+=*)$",Pattern.CASE_INSENSITIVE);//是否允许从表单编码的请求体参数中获取 Token。private boolean allowFormEncodedBodyParameter = false;//是否允许从 URI 查询参数中获取 Tokenprivate boolean allowUriQueryParameter = true;//存储 Bearer Token 的请求头名称,默认为 Authorization//常量值public static final String AUTHORIZATION = "Authorization";private String bearerTokenHeaderName = HttpHeaders.AUTHORIZATION;//用于检查当前请求路径是否应被忽略的路径匹配器private final PathMatcher pathMatcher = new AntPathMatcher();//存储可忽略 URL 列表private final PermitAllUrlProperties urlProperties;//构造器传入属性配置类:PermitAllUrlProperties(存储对外暴露的接口列表)public PigxBearerTokenExtractor(PermitAllUrlProperties urlProperties) {this.urlProperties = urlProperties;}//对token的抽取方法@Overridepublic String resolve(HttpServletRequest request) {//获取当前请求的urlString requestUri = request.getRequestURI();//去除上下文,获得相对路径String relativePath = requestUri.substring(request.getContextPath().length());//当前请求路径是否忽略boolean match = urlProperties.getIgnoreUrls().stream().anyMatch(url -> pathMatcher.match(url, relativePath));//当前请求路径忽略,返回nullif (match) {return null;}//通过resolveFromAuthorizationHeader方法获取token 详情见2.3.1final String authorizationHeaderToken = resolveFromAuthorizationHeader(request);//通过isParameterTokenSupportedForRequest方法从请求参数中解析出 Bearer Token,并返回 Token 字符串详情见2.3.2//通过isParameterTokenSupportedForRequest 判断当前请求是否支持从请求参数中获取 Token 详情见2.3.3final String parameterToken = isParameterTokenSupportedForRequest(request)? resolveFromRequestParameters(request) : null;//请求头中获取到tokenif (authorizationHeaderToken != null) {//请求参数中也有token,则抛出重复tokenif (parameterToken != null) {final BearerTokenError error = BearerTokenErrors.invalidRequest("Found multiple bearer tokens in the request");throw new OAuth2AuthenticationException(error);}//返回请求头中的tokenreturn authorizationHeaderToken;}//检测是否支持参数中获取token(详情见2.3.4),并且判断参数中是否有token//如果支持参数中获取token,并且参数中有token则返回参数中的tokenif (parameterToken != null && isParameterTokenEnabledForRequest(request)) {return parameterToken;}return null;}//详情2.3.1 从请求头中解析出 Bearer Token,并返回 Token 字符串private String resolveFromAuthorizationHeader(HttpServletRequest request) {//从请求头中获取请求头名称,默认为 Authorization的值String authorization = request.getHeader(this.bearerTokenHeaderName);//不以不区分大小写的方式以 "bearer" 开头,则返回 null,表示未找到有效的 Bearer Tokenif (!StringUtils.startsWithIgnoreCase(authorization, "bearer")) {return null;}//通过正则表达式对 authorization 进行匹配Matcher matcher = authorizationPattern.matcher(authorization);//果匹配失败,即 Bearer Token 格式不正确,则抛出 OAuth2AuthenticationException 异常,异常信息为 "Bearer token is malformed"if (!matcher.matches()) {BearerTokenError error = BearerTokenErrors.invalidToken("Bearer token is malformed");throw new OAuth2AuthenticationException(error);}//匹配成功,通过 matcher.group("token") 方法提取出 Token 字符串,并返回return matcher.group("token");}//详情2.3.2 从请求参数中解析出 Bearer Token,并返回 Token 字符串private static String resolveFromRequestParameters(HttpServletRequest request) {//通过 request.getParameterValues("access_token") 方法获取名为 "access_token" 的请求参数的值,存储在 values 数组中String[] values = request.getParameterValues("access_token");//如果 values 为 null 或长度为 0,则返回 null,表示未找到有效的 Bearer Tokenif (values == null || values.length == 0) {return null;}//如果 values 的长度为 1,则直接返回第一个值(默认取第一个),即 Token 字符串if (values.length == 1) {return values[0];}//如果 values 的长度大于 1,表示请求中包含多个 Bearer Token,此时抛出 OAuth2AuthenticationException 异常,异常信息为 "Found multiple bearer tokens in the request"BearerTokenError error = BearerTokenErrors.invalidRequest("Found multiple bearer tokens in the request");throw new OAuth2AuthenticationException(error);}//详情2.3.3 判断当前请求是否支持从请求参数中获取 Tokenprivate boolean isParameterTokenSupportedForRequest(final HttpServletRequest request) {return (("POST".equals(request.getMethod())&& MediaType.APPLICATION_FORM_URLENCODED_VALUE.equals(request.getContentType()))|| "GET".equals(request.getMethod()));}//详情2.3.4该方法的作用是判断是否允许在当前请求中通过请求参数获取 Tokenprivate boolean isParameterTokenEnabledForRequest(final HttpServletRequest request) {/*满足情况:1、allowFormEncodedBodyParameter 的值是否为 true,并且当前请求方法为 "POST",且请求的 Content-Type 为 "application/x-www-form-urlencoded2、allowUriQueryParameter 的值是否为 true,并且当前请求方法为 "GET"*/return ((this.allowFormEncodedBodyParameter && "POST".equals(request.getMethod())&& MediaType.APPLICATION_FORM_URLENCODED_VALUE.equals(request.getContentType()))|| (this.allowUriQueryParameter && "GET".equals(request.getMethod())));} }
2.4、资源服务器异常处理resourceAuthExceptionEntryPoint
/*** 资源服务器异常处理* @param objectMapper jackson 输出对象* @param securityMessageSource 自定义国际化处理器* @return ResourceAuthExceptionEntryPoint*/
@Bean
public ResourceAuthExceptionEntryPoint resourceAuthExceptionEntryPoint(ObjectMapper objectMapper,MessageSource securityMessageSource) {return new ResourceAuthExceptionEntryPoint(objectMapper, securityMessageSource);
}
具体内容解析
/*** @author lengleng* @date 2019/2/1** 客户端异常处理 AuthenticationException 不同细化异常处理*///全参构造器,会生成一个带有所有 final 字段的构造函数
@RequiredArgsConstructor
public class ResourceAuthExceptionEntryPoint implements AuthenticationEntryPoint {//进行 JSON 序列化private final ObjectMapper objectMapper;//国际化消息处理private final MessageSource messageSource;@Override@SneakyThrows //@SneakyThrows 是 Lombok 提供的注解,用于在方法上抛出异常时,自动将该异常包装为 RuntimeException 抛出public void commence(HttpServletRequest request, HttpServletResponse response,AuthenticationException authException) {/// 设置响应的字符编码为UTF8,内容类型为 JSON response.setCharacterEncoding(CommonConstants.UTF8);response.setContentType(ContentType.JSON.getValue());//创建一个封装错误信息的对象R<String> result = new R<>();//设置code为失败//常量:Integer FAIL = 1;result.setCode(CommonConstants.FAIL);//设置响应状态码为未授权401//UNAUTHORIZED(401, HttpStatus.Series.CLIENT_ERROR, "Unauthorized"),response.setStatus(HttpStatus.UNAUTHORIZED.value()); 如果存在认证异常,设置错误消息为 "error",数据为认证异常的消if (authException != null) {result.setMsg("error");result.setData(authException.getMessage());}// 针对令牌过期返回特殊的 424if (authException instanceof InvalidBearerTokenException|| authException instanceof InsufficientAuthenticationException) {//设置响应状态码为 424(FAILED_DEPENDENCY)//FAILED_DEPENDENCY(424, HttpStatus.Series.CLIENT_ERROR, "Failed Dependency")response.setStatus(HttpStatus.FAILED_DEPENDENCY.value()); 设置特定的错误消息 result.setMsg(this.messageSource.getMessage("OAuth2ResourceOwnerBaseAuthenticationProvider.tokenExpired",null, LocaleContextHolder.getLocale()));//如果用户令牌过期 修改coderesult.setCode(TOKEN_EXPIRED_FAIL);}//获取响应的输出流,通过该输出流可以向客户端发送数据PrintWriter printWriter = response.getWriter();//使用 Jackson 的 ObjectMapper 将 result 对象序列化为 JSON 格式的字符串//将序列化后的 JSON 字符串添加到输出流中,以便将其发送给客户端printWriter.append(objectMapper.writeValueAsString(result));}}
2.5、资源服务器toke内省处理器opaqueTokenIntrospector
自定义认证器,用于通过传递的令牌进行身份验证
/*** 资源服务器toke内省处理器* @param authorizationService token 存储实现* @return TokenIntrospector*/
@Bean
public OpaqueTokenIntrospector opaqueTokenIntrospector(OAuth2AuthorizationService authorizationService) {return new PigxCustomOpaqueTokenIntrospector(authorizationService);
}
具体解析
/*** @author lengleng* @date 2022/5/28*/
@Slf4j
@RequiredArgsConstructor
public class PigxCustomOpaqueTokenIntrospector implements OpaqueTokenIntrospector {private final OAuth2AuthorizationService authorizationService;//用于根据传递的令牌进行身份验证@Overridepublic OAuth2AuthenticatedPrincipal introspect(String token) {//通过OAuth2AuthorizationService的实现类去获取对应的token 详情见2.5.1OAuth2Authorization oldAuthorization = authorizationService.findByToken(token, OAuth2TokenType.ACCESS_TOKEN);//如果找不到与令牌关联的授权信息,则抛出 InvalidBearerTokenException 异常,表示令牌无效if (Objects.isNull(oldAuthorization)) {throw new InvalidBearerTokenException(token);}// 客户端模式默认返回//判断授权类型是否为客户端模式//public static final AuthorizationGrantType CLIENT_CREDENTIALS = new AuthorizationGrantType("client_credentials");if (AuthorizationGrantType.CLIENT_CREDENTIALS.equals(oldAuthorization.getAuthorizationGrantType())) {//默认返回一个 PigxClientCredentialsOAuth2AuthenticatedPrincipal 对象。该对象包含了传递的授权信息的属性、空权限列表以及授权主体名称。return new PigxClientCredentialsOAuth2AuthenticatedPrincipal(oldAuthorization.getAttributes(),AuthorityUtils.NO_AUTHORITIES, oldAuthorization.getPrincipalName());}//如果授权类型不是客户端模式,则获取所有实现了 PigxUserDetailsService 接口的 Bean 对象,并过滤出支持当前授权信息的 PigxUserDetailsService 对象//这里会获取到对应的PigxUserDetailsService的实现类Map<String, PigxUserDetailsService> userDetailsServiceMap = SpringContextHolder.getBeansOfType(PigxUserDetailsService.class);//选择支持度最高的 PigxUserDetailsService 对象(根据 Ordered 接口的顺序进行比较)Optional<PigxUserDetailsService> optional = userDetailsServiceMap.values().stream().filter(service -> service.support(Objects.requireNonNull(oldAuthorization).getRegisteredClientId(),oldAuthorization.getAuthorizationGrantType().getValue())).max(Comparator.comparingInt(Ordered::getOrder));//获取用户信息UserDetails userDetails = null;try {Object principal = Objects.requireNonNull(oldAuthorization).getAttributes().get(Principal.class.getName());UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = (UsernamePasswordAuthenticationToken) principal;Object tokenPrincipal = usernamePasswordAuthenticationToken.getPrincipal();userDetails = optional.get().loadUserByUser((PigxUser) tokenPrincipal);}catch (UsernameNotFoundException notFoundException) {log.warn("用户不不存在 {}", notFoundException.getLocalizedMessage());throw notFoundException;}catch (Exception ex) {log.error("资源服务器 introspect Token error {}", ex.getLocalizedMessage());}// 注入客户端信息,方便上下文中获取PigxUser pigxUser = (PigxUser) userDetails;Objects.requireNonNull(pigxUser).getAttributes().put(SecurityConstants.CLIENT_ID,oldAuthorization.getRegisteredClientId());return pigxUser;}}
2.5.1 authorizationService.findByToken(token, OAuth2TokenType.ACCESS_TOKEN);
其实现类有三个,我们用的是Pix提供的实现类PigxRedisOAuth2AuthorizationService

其中的方法如下,即从redis中去获取对应的token信息
@Override
@Nullable
public OAuth2Authorization findByToken(String token, @Nullable OAuth2TokenType tokenType) {Assert.hasText(token, "token cannot be empty");Assert.notNull(tokenType, "tokenType cannot be empty");redisTemplate.setValueSerializer(RedisSerializer.java());return (OAuth2Authorization) redisTemplate.opsForValue().get(buildKey(tokenType.getValue(), token));
}
3、PigxResourceServerConfiguration 资源服务器认证授权配置
@Slf4j
@EnableWebSecurity
@EnableMethodSecurity
@RequiredArgsConstructor
public class PigxResourceServerConfiguration {protected final ResourceAuthExceptionEntryPoint resourceAuthExceptionEntryPoint;private final PermitAllUrlProperties permitAllUrl;private final PigxBearerTokenExtractor pigxBearerTokenExtractor;private final OpaqueTokenIntrospector customOpaqueTokenIntrospector;@Bean@Order(Ordered.HIGHEST_PRECEDENCE)SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {AntPathRequestMatcher[] requestMatchers = permitAllUrl.getIgnoreUrls().stream().map(AntPathRequestMatcher::new).collect(Collectors.toList()).toArray(new AntPathRequestMatcher[] {});http.authorizeHttpRequests(authorizeRequests -> authorizeRequests.requestMatchers(requestMatchers).permitAll().anyRequest().authenticated()).oauth2ResourceServer(oauth2 -> oauth2.opaqueToken(token -> token.introspector(customOpaqueTokenIntrospector)).authenticationEntryPoint(resourceAuthExceptionEntryPoint).bearerTokenResolver(pigxBearerTokenExtractor)).headers(headers -> headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable)).csrf(AbstractHttpConfigurer::disable);return http.build();}}
这段代码是一个 Java 类 PigxResourceServerConfiguration,它配置了 Spring Security 的资源服务器。
首先,类中定义了一些依赖注入的属性:
resourceAuthExceptionEntryPoint:用于处理资源服务器的异常入口点。permitAllUrl:用于配置允许所有请求的 URL 列表。pigxBearerTokenExtractor:用于从请求中提取 Bearer Token。customOpaqueTokenIntrospector:自定义的不透明令牌内省器。
接下来,使用 @Bean 注解标记了一个方法 securityFilterChain,该方法返回一个 SecurityFilterChain 对象。该方法的作用是配置 Spring Security 的安全过滤器链。
在 securityFilterChain 方法中,首先根据 permitAllUrl 中的忽略 URL 列表创建了一个 AntPathRequestMatcher 数组 requestMatchers。这里使用了 Stream API 将忽略 URL 列表转换为 AntPathRequestMatcher 数组。
然后,通过调用 authorizeHttpRequests() 方法配置了请求的授权规则。其中,使用 requestMatchers(requestMatchers).permitAll().anyRequest().authenticated() 来配置了忽略 URL 列表的请求允许访问,而其他请求需要进行身份验证。
接着,使用 oauth2ResourceServer() 方法配置了 OAuth2 资源服务器。通过调用 opaqueToken() 方法设置了自定义的不透明令牌内省器,并使用 bearerTokenResolver() 方法设置了用于解析 Bearer Token 的 pigxBearerTokenExtractor。
继续,使用 headers() 方法配置了 HTTP 头部,通过调用 frameOptions() 方法禁用了 X-Frame-Options。
最后,使用 csrf() 方法禁用了 CSRF(跨站请求伪造)保护,并调用 http.build() 方法构建并返回了安全过滤器链。
这段代码的作用是配置 Spring Security 的资源服务器,定义了请求的授权规则、OAuth2 资源服务器和一些其他配置。
4、PigxFeignClientConfiguration.class
public class PigxFeignClientConfiguration {/*** 注入 oauth2 feign token 增强* @param tokenResolver token获取处理器* @return 拦截器*/@Beanpublic RequestInterceptor oauthRequestInterceptor(BearerTokenResolver tokenResolver) {return new PigxOAuthRequestInterceptor(tokenResolver);}@Beanpublic RequestInterceptor clientToCRequestInterceptor() {return new PigxClientToCRequestInterceptor();}}
4.1 、oauthRequestInterceptor方法
该类的作用是在发送请求之前拦截并修改请求模板(RequestTemplate)
/*** 注入 oauth2 feign token 增强* @param tokenResolver token获取处理器* @return 拦截器*/
@Bean
public RequestInterceptor oauthRequestInterceptor(BearerTokenResolver tokenResolver) {return new PigxOAuthRequestInterceptor(tokenResolver);
}
具体详解:
/*** oauth2 feign token传递** 重新 OAuth2FeignRequestInterceptor ,官方实现部分常见不适用** @author lengleng* @date 2022/5/29*/
@Slf4j
@RequiredArgsConstructor
public class PigxOAuthRequestInterceptor implements RequestInterceptor {private final BearerTokenResolver tokenResolver;/*** Create a template with the header of provided name and extracted extract </br>** 1. 如果使用 非web 请求,header 区别 </br>** 2. 根据authentication 还原请求token* @param template*/@Overridepublic void apply(RequestTemplate template) {Collection<String> fromHeader = template.headers().get(SecurityConstants.FROM);// 带from 请求直接跳过if (CollUtil.isNotEmpty(fromHeader) && fromHeader.contains(SecurityConstants.FROM_IN)) {return;}// 非web 请求直接跳过if (WebUtils.getRequest() == null) {return;}HttpServletRequest request = WebUtils.getRequest();// 避免请求参数的 query token 无法传递String token = tokenResolver.resolve(request);if (StrUtil.isBlank(token)) {return;}//添加token信息template.header(HttpHeaders.AUTHORIZATION,String.format("%s %s", OAuth2AccessToken.TokenType.BEARER.getValue(), token));}
}
4.2、 clientToCRequestInterceptor方法
@Bean
public RequestInterceptor clientToCRequestInterceptor() {return new PigxClientToCRequestInterceptor();
}
具体详解:
/*** TOC 客户标识传递** @author lengleng* @date 2023/3/17*/
@Slf4j
public class PigxClientToCRequestInterceptor implements RequestInterceptor {/*** Called for every request. Add data using methods on the supplied* {@link RequestTemplate}.* @param template*/public void apply(RequestTemplate template) {String reqVersion = WebUtils.getRequest() != null? WebUtils.getRequest().getHeader(SecurityConstants.HEADER_TOC) : null;if (StrUtil.isNotBlank(reqVersion)) {log.debug("feign add header toc :{}", reqVersion);template.header(SecurityConstants.HEADER_TOC, reqVersion);}}}
相关文章:
PIG框架学习2——资源服务器的配置详解
一、前言 1、pig资源服务器的配置 Spring Security oauth2相关的依赖是在pigx-common-security模块中引入的,其他模块需要进行token鉴权的,需要在微服务中引入pigx-common-security模块的依赖,从而间接引入相关的Spring security oauth2依赖…...
vue+element ui实现图片上传并拖拽进行图片排序
用到的技术栈: vue2element Uivue-dragging 如何使用: 第一步: 安装 npm install awe-dnd --save第二步: 引入 main.js 文件 // 引入组件 import VueDND from awe-dnd // 添加至全局 Vue.use(VueDND)具体项目代码 <el-form-item label"封面…...
国产服务器 BIOS下组建RADI不同RAID卡-超详细
国产服务器 长城 组建Raid的方法 说明 大多数国产服务器通用型服务器进入BIOS的都是按DEL键。 9361RAID卡组建方法 在服务器启动过程中,按下DEL键进入BIOS界面。 进入设备管理器,选择AVAGO MegaRAID页签。 3. 进入RAID卡设备,选择Main Me…...
UE4 4.21-4.27使用编辑器蓝图EditorBlueprint方法
在UE4 4.21中,编辑器蓝图(Editor Blueprint)是一个强大的工具,允许开发者扩展和自定义Unreal编辑器的功能。通过编辑器蓝图,我们可以创建自定义的工具和功能,以优化开发流程。 本教程将指导您如何在UE4 4.…...
105、Zero-1-to-3: Zero-shot One Image to 3D Object
简介 官网 使用合成数据集来学习相对摄像机视点的控制,这允许在指定的摄像机变换下生成相同对象的新图像,用于从单个图像进行三维重建的任务。 实现流程 输入图像 x ∈ R H W 3 x \in \R^{H \times W \times 3} x∈RHW3,所需视点的相…...
scala 安装和创建项目
Scala,一种可随您扩展的编程语言:从小型脚本到大型多平台应用程序。Scala不是Java的扩展,但它完全可以与Java互操作。在编译时,Scala文件将转换为Java字节码并在JVM(Java虚拟机)上运行。Scala被设计成面向对…...
Python办公自动化 – 自动化文本翻译和Oracle数据库操作
Python办公自动化 – 自动化文本翻译和Oracle数据库操作 以下是往期的文章目录,需要可以查看哦。 Python办公自动化 – Excel和Word的操作运用 Python办公自动化 – Python发送电子邮件和Outlook的集成 Python办公自动化 – 对PDF文档和PPT文档的处理 Python办公自…...
如何在Win10电脑接收苹果手机日程提醒呢?
有很多小伙伴手机使用的是iPhone苹果手机,但办公电脑使用的win10系统的电脑,这时候如果想要在win10电脑上同步接收苹果手机上设置的日程提醒,该怎么操作呢?如何在win10电脑接收苹果手机日程提醒呢? 如果你设置的日程提…...
227.【2023年华为OD机试真题(C卷)】小明找位置(二分查找-JavaPythonC++JS实现)
🚀点击这里可直接跳转到本专栏,可查阅顶置最新的华为OD机试宝典~ 本专栏所有题目均包含优质解题思路,高质量解题代码(Java&Python&C++&JS分别实现),详细代码讲解,助你深入学习,深度掌握! 文章目录 一. 题目-小明找位置二.解题思路三.题解代码Python题解代…...
【现代密码学】笔记3.4-3.7--构造安全加密方案、CPA安全、CCA安全 《introduction to modern cryphtography》
【现代密码学】笔记3.4-3.7--构造安全加密方案、CPA安全、CCA安全 《introduction to modern cryphtography》 写在最前面私钥加密与伪随机性 第二部分流加密与CPA多重加密 CPA安全加密方案CPA安全实验、预言机访问(oracle access) 操作模式伪随机函数PR…...
服务器带宽有什么用? 带宽不足怎么办?
服务器带宽是指服务器能够接收和传输数据的速率,通常以每秒传输的数据量来衡量。它是支持特定应用服务器网络和因特网(Internet)访问的单一网络线路,对网络速度、响应时间、应用程序处理速度等方面都有影响。 服务器带宽有什么作…...
Alphafold2蛋白质结构预测AI工作站配置推荐
AlphaFold2计算特点 蛋白质三维结构预测是一项计算量非常巨大的任务,科学家多年的探索研究,形成了X射线晶体学法、核磁共振法、冷冻电镜等。 2021年底,谷歌的DeepMind团队的采用人工智能方法的AlphaFold2算法在生物界引起了极大的轰动…...
如何让ArcGIS Pro启动显示空白页面
刚接触ArcGIS Pro的你是否会觉得在操作上有那么一些不习惯,从一开始软件启动就发现和ArcGIS差距很大:丰富的欢迎页面,加上默认加载的地图让你眼花缭乱,这里教你如何去掉这些繁杂的内容,还你一个干净的启动页面。 跳过…...
超市账单管理系统产品数据新增Servlet实现
超市账单管理系统产品数据新增Servlet实现 package com.test.controller; import java.io.IOException; import java.util.List; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import ja…...
计算机组成原理之计算机硬件发展和计算机系统的组成
学习的最大理由是想摆脱平庸,早一天就多一份人生的精彩;迟一天就多一天平庸的困扰。各位小伙伴,如果您: 想系统/深入学习某技术知识点… 一个人摸索学习很难坚持,想组团高效学习… 想写博客但无从下手,急需…...
《JVM由浅入深学习【七】 2024-01-11》JVM由简入深学习提升分享
亲爱的读者们,欢迎来到本篇博客,这是JVM第七次分享,下面是七个JVM常用常面的分享,请笑纳 目录 1. 几个与JVM 内存相关的核心参数2.如何计算一个对象的大小3.堆为什么要分为新生代和老年代4.JVM堆的年轻代为什么要有两个 Survivor…...
Golang leetcode142 环形链表 暴力map 快慢指针法
文章目录 环形链表 leetcode142暴力遍历 map哈希记录快慢指针法 环形链表 leetcode142 该题目要求找到入环的第一个节点 我们可以通过map进行记录,没到新的节点查询是否经过原有节点 入环节点,上两个节点的next相同 若有入环节点,则一定能检…...
基于java,springboot的论旅游管理系统设计与实现
环境以及简介 基于java,springboot的论旅游管理系统设计与实现,Java项目,SpringBoot项目,含开发文档,源码,数据库以及ppt 源码下载 环境配置: 框架:springboot JDK版本:JDK1.8 服…...
掌握视频节奏,玩转剪辑艺术!,轻松调整视频播放速度与秒数的技巧大揭秘
你是否经常觉得视频播放得太快或太慢,无法满足你的观看需求?或者想要控制视频的长度,却不知道该如何下手?今天,我们将为你揭秘几种简单又实用的方法,让你轻松调整视频的播放速度和秒数! 首先&a…...
51单片机介绍
1 单片机简介 单片机,英文Micro Controller Unit,简称MCU 内部集成了CPU、RAM、ROM、定时器、中断系统、通讯接口等一系列电脑的常用硬件功能 单片机的任务是信息采集(依靠传感器)、处理(依靠CPU)和硬件设…...
IDEA运行Tomcat出现乱码问题解决汇总
最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…...
Neo4j 集群管理:原理、技术与最佳实践深度解析
Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...
大学生职业发展与就业创业指导教学评价
这里是引用 作为软工2203/2204班的学生,我们非常感谢您在《大学生职业发展与就业创业指导》课程中的悉心教导。这门课程对我们即将面临实习和就业的工科学生来说至关重要,而您认真负责的教学态度,让课程的每一部分都充满了实用价值。 尤其让我…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
逻辑回归暴力训练预测金融欺诈
简述 「使用逻辑回归暴力预测金融欺诈,并不断增加特征维度持续测试」的做法,体现了一种逐步建模与迭代验证的实验思路,在金融欺诈检测中非常有价值,本文作为一篇回顾性记录了早年间公司给某行做反欺诈预测用到的技术和思路。百度…...
sshd代码修改banner
sshd服务连接之后会收到字符串: SSH-2.0-OpenSSH_9.5 容易被hacker识别此服务为sshd服务。 是否可以通过修改此banner达到让人无法识别此服务的目的呢? 不能。因为这是写的SSH的协议中的。 也就是协议规定了banner必须这么写。 SSH- 开头,…...
向量几何的二元性:叉乘模长与内积投影的深层联系
在数学与物理的空间世界中,向量运算构成了理解几何结构的基石。叉乘(外积)与点积(内积)作为向量代数的两大支柱,表面上呈现出截然不同的几何意义与代数形式,却在深层次上揭示了向量间相互作用的…...
渗透实战PortSwigger Labs指南:自定义标签XSS和SVG XSS利用
阻止除自定义标签之外的所有标签 先输入一些标签测试,说是全部标签都被禁了 除了自定义的 自定义<my-tag onmouseoveralert(xss)> <my-tag idx onfocusalert(document.cookie) tabindex1> onfocus 当元素获得焦点时(如通过点击或键盘导航&…...
【大模型】RankRAG:基于大模型的上下文排序与检索增强生成的统一框架
文章目录 A 论文出处B 背景B.1 背景介绍B.2 问题提出B.3 创新点 C 模型结构C.1 指令微调阶段C.2 排名与生成的总和指令微调阶段C.3 RankRAG推理:检索-重排-生成 D 实验设计E 个人总结 A 论文出处 论文题目:RankRAG:Unifying Context Ranking…...
PLC入门【4】基本指令2(SET RST)
04 基本指令2 PLC编程第四课基本指令(2) 1、运用上接课所学的基本指令完成个简单的实例编程。 2、学习SET--置位指令 3、RST--复位指令 打开软件(FX-TRN-BEG-C),从 文件 - 主画面,“B: 让我们学习基本的”- “B-3.控制优先程序”。 点击“梯形图编辑”…...
