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

pig4cloudSpring 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。hasPermissionpms 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);}}}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/612417.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

20240110在ubuntu20.04下重启samba服务

20240110在ubuntu20.04下重启samba服务 百度搜索&#xff1a;samba restart https://www.python100.com/html/78028.html 重启samba命令详解 更新&#xff1a;2023-05-17 16:04 一、重启samba命令 重启samba可以使用以下命令&#xff1a; /etc/init.d/smb restart 或者 syste…

中国智造闪耀CES | 木牛科技在美国CES展亮相多领域毫米波雷达尖端方案

素有全球科技潮流“风向标”之称的2024国际消费类电子产品展&#xff08;CES&#xff09;&#xff0c;于1月9-12日在美国拉斯维加斯会议中心举办。CES是全球最大的消费电子和消费技术展览会之一&#xff0c;汇集了世界各地优秀的消费电子和科技公司&#xff0c;带着最好的产品来…

uniapp项目怎么删除顶部导航栏

uniapp去掉顶部导航的方法&#xff1a; 1、去掉所有导航栏 "globalStyle": { "navigationBarTextStyle": "white", "navigationBarTitleText": "uni-app", "navigationBarBackgroundColor": "#007AFF"…

Apache ActiveMQ RCE CNVD-2023-69477 CVE-2023-46604

漏洞简介 Apache ActiveMQ官方发布新版本&#xff0c;修复了一个远程代码执行漏洞&#xff0c;攻击者可构造恶意请求通过Apache ActiveMQ的61616端口发送恶意数据导致远程代码执行&#xff0c;从而完全控制Apache ActiveMQ服务器。 影响版本 Apache ActiveMQ 5.18.0 before 5.1…

Windows系统下python版本Open3D-0.18.0 的快速安装与使用

目录 一、安装Anaconda3二、安装open3d三、测试代码四、结果展示五、测试数据 Windows系统下python版本Open3D-0.18.0 的快速安装与使用由CSDN点云侠原创&#xff0c;爬虫自重。如果你不是在点云侠的博客中看到该文章&#xff0c;那么此处便是不要脸的爬虫。 一、安装Anaconda…

Hive基础知识(八):Hive对数据库的增删改查操作

1. 创建数据库 CREATE DATABASE [IF NOT EXISTS] database_name [COMMENT database_comment]#注释 [LOCATION hdfs_path]#指定当前库的hdfs目录 [WITH DBPROPERTIES (property_nameproperty_value,...)]; #备注创建作者和创建时间 1&#xff09;创建一个数据库&#xff0c;数据…

Linux 常用进阶指令

我是南城余&#xff01;阿里云开发者平台专家博士证书获得者&#xff01; 欢迎关注我的博客&#xff01;一同成长&#xff01; 一名从事运维开发的worker&#xff0c;记录分享学习。 专注于AI&#xff0c;运维开发&#xff0c;windows Linux 系统领域的分享&#xff01; 其他…

最实用的 8 个免费 Android 数据恢复软件

如果您正在寻找最好的免费 Android 数据恢复软件&#xff0c;那就不用再犹豫了&#xff0c;因为我已经列出了最好的软件。不可否认&#xff0c;智能手机和平板电脑等 Android 设备正在与技术一起发展。与以前相比&#xff0c;它们也更加融入了我们的日常生活。 Android 智能手…

Golang,Gin框架使用ShouldBindJSON时出现EOF报错

前言 做毕设ing&#xff0c;使用的是Gin框架做的一个简单的管理系统。但偶尔也会碰到一些稀奇古怪的Bug&#xff0c;因此记录一下。 问题描述 api是这样写的 func UserRegisterHandler(c *gin.Context, user *usecase.UserOperate) {if err : c.ShouldBindJSON(&UserReg…

七种查找算法,十大排序算法

文章目录 常见的七种查找算法&#xff1a;1. 基本查找2. 二分查找3. 插值查找4. 斐波那契查找5. 分块查找6. 哈希查找7. 树表查找 十大排序算法&#xff1a;1. 冒泡排序1.1 算法步骤1.2 动图演示1.3 代码示例 2. 选择排序2.1 算法步骤2.2 动图演示 3. 插入排序3.1 算法步骤3.2 …

数据结构与算法教程,数据结构C语言版教程!(第三部分、栈(Stack)和队列(Queue)详解)三

第三部分、栈(Stack)和队列(Queue)详解 栈和队列&#xff0c;严格意义上来说&#xff0c;也属于线性表&#xff0c;因为它们也都用于存储逻辑关系为 "一对一" 的数据&#xff0c;但由于它们比较特殊&#xff0c;因此将其单独作为一章&#xff0c;做重点讲解。 使用栈…

2022-2023 ICPC, Asia Yokohama Regional Contest 2022(题解)

2022-2023 ICPC, Asia Yokohama Regional Contest 2022 文章目录 A. Hasty Santa ClausB. Interactive Number GuessingC. Secure the Top SecretD. Move One CoinE. Incredibly Cute Penguin ChicksF. Make a LoopG. Remodeling the DungeonH. Cake DecorationI. Quiz Contest…

【申请SSL证书】免费申请阿里云SSL证书

注意&#xff1a;申请 SSL证书的前提是有一个域名且备案了 第一部&#xff1a;申请免费证书 免费 CA 证书购买地址&#xff08;请戳这里&#xff09; 选择合适的选项如下图 为了解决免费证书近期存在的吊销、统计等问题&#xff0c;自2021年起&#xff0c;免费证书申请申请将…

鸿蒙Harmony--状态管理器--@Prop详解

纵横千里独行客&#xff0c;何惧前路雨潇潇。夜半浊酒慰寂寞&#xff0c;天明走马入红尘。且将新火试新茶&#xff0c;诗酒趁年华。青春以末&#xff0c;壮志照旧&#xff0c;生活以悟&#xff0c;前路未明。时间善变&#xff0c;可执着翻不了篇。时光磨我少年心&#xff0c;却…

YOLOv5改进 | 检测头篇 | DynamicHead支持检测和分割(不同于网上版本,全网首发)

一、本文介绍 本文给大家带来的改进机制是DynamicHead(Dyhead),这个检测头由微软提出的一种名为“动态头”的新型检测头,用于统一尺度感知、空间感知和任务感知。网络上关于该检测头我查了一些有一些魔改的版本,但是我觉得其已经改变了该检测头的本质,因为往往一些细节上才…

【读书笔记】学习突围

最近在读一本书《学习突围》&#xff0c;作者是常青&#xff0c;知乎大V。对他的一些回答非常认同&#xff0c;受益匪浅&#xff0c;特此买来纸质书籍细细学习一番&#xff01; 1.【学习心态】&#xff08;拖延症、自控、执行力、专注力&#xff09; 2.【学习方法】&#xff0…

2024.1.11每日一题

LeetCode 2645.构造有效字符串的最少插入数 2645. 构造有效字符串的最少插入数 - 力扣&#xff08;LeetCode&#xff09; 题目描述 给你一个字符串 word &#xff0c;你可以向其中任何位置插入 “a”、“b” 或 “c” 任意次&#xff0c;返回使 word 有效 需要插入的最少字…

Java Http各个请求类型详细介绍

1. 前言 在Spring Boot框架中&#xff0c;HTTP请求类型是构建Web应用程序的重要组成部分。常见的请求类型包括GET、POST、PUT和DELETE&#xff0c;每种类型都有其特定的用途和特点。本文将详细比较这四种请求类型&#xff0c;帮助您在开发过程中做出明智的选择。 2. GET请求…

order by之后的injection(sqllabs第四十六关)

order by相关注入知识 这一关的sql语句是利用的order by 根据输入的id不同数据排序不一样可以确定就是order by order by后面无法使用ubion注入&#xff08;靠找不到&#xff09; 可以利用后面的参数进行攻击 1&#xff09;数字 没作用考虑布尔类型 rand和select ***都可以 …

Linux_Centos7安装snmp服务

Linux_Centos7安装snmp服务 1.背景2.目的3.环境4.操作4.1 手动安装snmp协议4.2 批量安装snmp协议 1.背景 收到云平台异常告警&#xff0c;提示ECS服务器在在一分钟内客户端存在多次ssh远程登陆&#xff0c;被判断为ssh远程破解&#xff0c;通过排查得出为运维系统配置过程中错…