Spring Cloud Oauth2 + Security 填坑记
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring Cloud Oauth2 + Security 填坑记相关的知识,希望对你有一定的参考价值。
参考技术A 教程: Spring Cloud下基于OAUTH2认证授权的实现Spring cloud微服务实战——基于OAUTH2.0统一认证授权的微服务基础架构
Spring Cloud OAuth2(一) 搭建授权服务
https://gitee.com/xingfly/Spring-CloudJiYuZuulDeTongYiShouQuanRenZheng
https://gitee.com/log4j/pig
搜索到一些文章,就照着搭,但是由于本人使用Spring Cloud 是Finchley.RELEASE版本,而教程或者demo通常是Dalston.SR3版本的。由于版本差异出现了一些问题。
Dalston.SR3版本spring-boot-starter-data-redis默认实现是Jedis,security oauth使用Redis存储token的实现类RedisTokenStore中使用Pipeline的功能。Jedis对于单节点可以支持Pipeline,但是集群则没有支持Pipeline,最终导致服务无法使用。
要解决这个问题,要么自己实现Jedis对Pipeline的支持,这比较复杂。
另一种就是使用lettuce代替Jedis,luttuce对于集群使用Pipeline有很好的支持。而且Dalston.SR3版本spring-boot-starter-data-redis也有相关的配置类,我们只需要引入luttuce的依赖:
注意luttuce的版本与spring的对应关系,错误的版本也导致无法启动。查找的时候发现不同的lettuce,groupId有不同的,连里面的包名也不同。
然后配置redisConnectionFactory:
各微服务单独做token校验时遇到了一些token传递的问题
发起请求时,需要添加Authorization请求头设置token完成校验,直接访问微服务没有问题,但是经过zuul网关转发时发现返回401。
排查发现在经过zuul转发到微服务时丢失了Authorization请求头,查找资料才知道zuul默认会过滤一些敏感header。
如果需要将token信息传递给微服务,则需要配置zuul关闭默认过滤:
这里冒号之后是空,表示没有需要过滤的header。
Feign本质上是一个新的请求,与进入这个微服务的请求并不是同一个。如果我们需要token传递给下游微服务,需要自己取出token设置给新请求。
先定义一个Feign的请求拦截器:
这里完成了取出token,设置的操作。注意 @Component 注解,然后将这个拦截器配置给FeignClient:
这样在构造Feign请求时会执行拦截器的操作,完成token的传递。
当使用hystrix时,你会发现上面Feign的拦截器中并不能获取到token。问题原因来自hystrix隔离策略,默认是线程隔离,也就是说Feign的请求执行在一个新的线程中。而SecurityContextHolder获取token对象是通过ThreadLocal存储的,也即是说与线程绑定,因此无法在新线程中获取token。
解决方法是将hystrix隔离策略(strategy)修改为:SEMAPHORE。
自定义oauth2验证失败出参
hasRole hasAuthority 区别
Spring Security OAuth2 授权失败(401) 问题整理
spring cloud gateway oauth 整合
https://gitee.com/owenwangwen/open-capacity-platform/tree/master/new-api-gateway
package com.open.capacity.client.filter;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@Component
public class AccessFilter implements GlobalFilter ,Ordered{
// url匹配器
private AntPathMatcher pathMatcher = new AntPathMatcher();
@Resource
private RedisTemplate<String, Object> redisTemplate ;
@Override
public int getOrder() {
// TODO Auto-generated method stub
return -500;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// TODO Auto-generated method stub
String accessToken = extractToken(exchange.getRequest());
if(pathMatcher.match("/**/v2/api-docs/**",exchange.getRequest().getPath().value())){
return chain.filter(exchange);
}
if(!pathMatcher.match("/api-auth/**",exchange.getRequest().getPath().value())){
if (accessToken == null) {
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}else{
try {
Map<String, Object> params = (Map<String, Object>) redisTemplate.opsForValue().get("token:" + accessToken) ;
if(params.isEmpty()){
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
} catch (Exception e) {
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
}
}
return chain.filter(exchange);
}
protected String extractToken(ServerHttpRequest request) {
List<String> strings = request.getHeaders().get("Authorization");
String authToken = null;
if (strings != null) {
authToken = strings.get(0).substring("Bearer".length()).trim();
}
if (StringUtils.isBlank(authToken)) {
strings = request.getQueryParams().get("access_token");
if (strings != null) {
authToken = strings.get(0);
}
}
return authToken;
}
}
以上是关于Spring Cloud Oauth2 + Security 填坑记的主要内容,如果未能解决你的问题,请参考以下文章
调用spring oauth2授权服务器时Spring Cloud Gateway卡住
Spring Cloud Security Oauth2集成
Spring Cloud Gateway OAuth2 with Spring Security OAuth2 Authorization Server = loop