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.rhkb.cn/news/233101.html

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

相关文章

一、二进制方式 安装部署K8S

目录 一、操作系统初始化 1、关闭防火墙 2、关闭 SELinu 3、 关闭 swap 4、添加hosts 5、同步系统时间 二、集群搭建 —— 使用外部Etcd集群 1、自签证书 2、自签 Etcd SSL 证书 ① 创建 CA 配置文件&#xff1a;ca-config.json ② 创建 CA 证书签名请求文件&#xff…

爬虫实战3-js逆向入门:以黑猫投诉平台为例

目录 引言 逆向过程 步骤一&#xff1a;找到参数对应js代码位置 步骤二&#xff1a;分析参数值的生成逻辑 步骤三&#xff1a;确定函数u的具体内容 步骤四&#xff1a;使用python实现请求参数的生成 投诉信息爬取 引言 下面是一张主流网页加密方法的思维导图&#xff0…

[SpringBoot]自定义注解@AutoFill,实现公共字段自动填充(避免重复对时间属性初始化

对于时间属性&#xff0c;如createTime、updateTime在进行插入、修改操作时都要一个个初始化处理&#xff0c;过于麻烦。 可以自定义注解AutoFill作用于INSERT&#xff0c;UPDATE操作方法上&#xff0c;再自定义切面类&#xff0c;统一拦截加入了AutoFill注解的方法&#xff0c…

Python爬虫获取百度的图片

一. 爬虫的方式&#xff1a; 主要有2种方式: ①ScrapyXpath (API 静态 爬取-直接post get) ②seleniumXpath (点击 动态 爬取-模拟) ScrapyXpath XPath 是 Scrapy 中常用的一种解析器&#xff0c;可以帮助爬虫定位和提取 HTML 或 XML 文档中的数据。 Scrapy 中使用 …

ctfshow——信息搜集

文章目录 web 1web 2web 3web 4web 5web 6web 7web 8web 9web 10web 11web 12web 13web 14web 15web 16web 17web 18web 19web 20 web 1 题目提示开发注释未及时删除。 直接右键查看源代码。 web 2 在这关我们会发现&#xff1a;1&#xff09;无法使用右键查看源代码&…

【亚马逊云科技】自家的AI助手 - Amazon Q

写在前面&#xff1a;博主是一只经过实战开发历练后投身培训事业的“小山猪”&#xff0c;昵称取自动画片《狮子王》中的“彭彭”&#xff0c;总是以乐观、积极的心态对待周边的事物。本人的技术路线从Java全栈工程师一路奔向大数据开发、数据挖掘领域&#xff0c;如今终有小成…

Neuro Contamination - Cyberpunk Gaming Music Futuristic Glitchy Sci-fi

无论是展示赛博朋克未来的电影场景&#xff0c;还是介绍高科技武器&#xff0c;你的音乐选择都至关重要。这首曲子的灵感来自科幻小说&#xff0c;旨在让你的观众想象未来的感觉。 潜在用例&#xff1a;科幻游戏、赛博朋克游戏、电影预告片、动作场景和产品广告。 非常适合充…

爆肝整理,接口测试+为什么要做接口测试总结,策底贯通...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 1、什么是接口测试…

【SpringBoot+dubbo+zk】实现服务之间rpc通信

0)前置准备&#xff0c;我们使用zk作为注册中心&#xff0c;先启动zk&#xff0c;也就是2181端口。 1)父工程pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http…

现有网络模型的使用及修改(VGG16为例)

VGG16 修改默认路径 import os os.environ[TORCH_HOME] rD:\Pytorch\pythonProject\vgg16 # 下载位置太大了&#xff08;140多G&#xff09;不提供直接下载 train_set torchvision.datasets.ImageNet(root./data_image_net, splittrain, downloadTrue, transformtorchvis…

GPDB - 高可用 - 流复制状态

GPDB - 高可用 - 流复制状态 GPDB的高可用基于流复制&#xff0c;通过FTS进行自动故障切换。自动故障切换需要根据primary-mirror流复制的各种状态进行判断。本节就聊聊primary-mirror流复制的各种状态。同样适用于PgSQL 1、WalSndState typedef enum WalSndState {WALSNDSTATE…

本地部署 Ollama

本地部署 Ollama 0. Ollama 能帮我们做什么1. 下载 Ollama2. 安装 Ollama3. 使用 Ollama4. Ollama 和 Langchain 的集成 0. Ollama 能帮我们做什么 在本地启动并运行大型语言模型。 运行 Llama 2、Code Llama 和其他模型。自定义并创建自己的。 1. 下载 Ollama 访问 https:…

【数据仓库与联机分析处理】数据仓库工具Hive

目录 一、Hive简介 &#xff08;一&#xff09;什么是Hive &#xff08;二&#xff09;优缺点 &#xff08;三&#xff09;Hive架构原理 &#xff08;四&#xff09;Hive 和数据库比较 二、MySQL的安装配置 三、Hive的安装配置 1、下载安装包 2、解压并改名 3、配置环…

ubuntu创建pytorch-gpu的docker环境

文章目录 安装docker创建镜像创建容器 合作推广&#xff0c;分享一个人工智能学习网站。计划系统性学习的同学可以了解下&#xff0c;点击助力博主脱贫( •̀ ω •́ )✧ 使用docker的好处就是可以将你的环境和别人的分开&#xff0c;特别是共用的情况下。本文介绍了ubuntu环境…

由浅入深理解C#中的事件

目录 本文较长&#xff0c;给大家提供了目录&#xff0c;可以直接看自己感兴趣的部分。 前言有关事件的概念示例​ 简单示例​ 标准 .NET 事件模式​ 使用泛型版本的标准 .NET 事件模式​ 补充总结 参考前言 前面介绍了C#中的委托&#xff0c;事件的很多部分都与委托…

Vue知识总结-中

VUE-生命周期 生命周期概述 生命周期也常常被称为生命周期回调函数/生命周期函数/生命周期钩子生命周期是Vue在关键时刻帮我们调用的一些特殊名称的函数生命周期函数的名字不能更改,但函数的具体内容是由我们程序员自己编写的生命周期函数中的this指向是vm或组件实例对象 生命周…

OpenCV | 光流估计

光流估计 光流是空间运动物体在观测成像平面上的像素运动的“瞬时速度”&#xff0c;根据各个像素点的速度的速度矢量特征&#xff0c;可以对图像进行动态分析&#xff0c;例如目标跟踪 高度恒定&#xff1a;同一点随着时间的变化&#xff0c;其亮度不会发生改变。小运动&…

Hive实战:网址去重

文章目录 一、实战概述二、提出任务三、完成任务&#xff08;一&#xff09;准备数据1、在虚拟机上创建文本文件2、上传文件到HDFS指定目录 &#xff08;二&#xff09;实现步骤1、启动Hive Metastore服务2、启动Hive客户端3、基于HDFS数据文件创建Hive外部表4、利用Hive SQL实…

JavaScript基础(24)_dom查询练习(一)

<!DOCTYPE html> <html lang"zh"> <head><meta charset"UTF-8"><link rel"stylesheet" href"../browser_default_style/reset.css"><title>dom查询练习一</title><style>.text {widt…

uniapp 微信小程序跳转外部链接

一、背景&#xff1a; 开发小程序时&#xff0c;跳转到内部路径通常会使用&#xff1a;uni.navigateTo&#xff0c;uni.redirectTo&#xff0c;uni.reLaunch&#xff0c;uni.switchTab等方法&#xff0c;可以跳转到pages.json中已经注册的页面 uni.navigateTo(OBJECT) | uni-…