module:auth

1.添加依赖:spring-cloud-starter-security与spring-cloud-starter-oauth2

2.配置WebSecurityConfig:注入AuthenticationManager覆写configure

3.配置tokenConfig

4.授权服务器配置:AuthorizationServer extends AuthorizationServerConfigurerAdapter配置客户端详细任务,令牌端点配置

5.实现UserDetailsService,覆写loadUserByUsername(最熟悉的一集)

tips:这里return 的UserDetails对象不用自己创建,springsecurity提供了一个User类实现了UserDetails,通过建造者模式可以直接生成username,password,authorities等信息。这里如果需要多参数分发给其他module使用,建议的做法是将数据库中查出来的对象通过json转成字符串存入username,后续可以直接将username反写成具体的class

module:gateway

首先明确网关的作用:路由转发、认证、白名单放行:针对当前网关的路由进行转发,如果是白名单则直接放行,如果是需要JWT校验则需要校验JWT合法性

security中提供了认证与授权,这里我们在网关进行了认证,也就是说授权模块是在各个微服务中进行的

gateway集成springsecurity:

1.添加依赖:spring-cloud-starter-security与spring-cloud-starter-oauth2

2.添加配置:过滤器;再添加tokenConfig、securityConfig


/**
*
* @description 网关认证过滤器:这段代码主要是作为过滤器去处理一个http请求,首先是检查请求路径是否在白名单中,请求是否携带token/有没有过期等安全相关检查
*/
@Component
@Slf4j
public class GatewayAuthFilter implements GlobalFilter, Ordered { //白名单
private static List<String> whitelist = null; static {
//加载白名单
try (
InputStream resourceAsStream = GatewayAuthFilter.class.getResourceAsStream("/security-whitelist.properties");
) {
Properties properties = new Properties();
properties.load(resourceAsStream);
Set<String> strings = properties.stringPropertyNames();
whitelist= new ArrayList<>(strings); } catch (Exception e) {
log.error("加载/security-whitelist.properties出错:{}",e.getMessage());
e.printStackTrace();
}
} @Autowired
private TokenStore tokenStore; @Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String requestUrl = exchange.getRequest().getPath().value();
AntPathMatcher pathMatcher = new AntPathMatcher();
//白名单放行
for (String url : whitelist) {
if (pathMatcher.match(url, requestUrl)) {
return chain.filter(exchange);
}
} //检查token是否存在
String token = getToken(exchange);
if (StringUtils.isBlank(token)) {
return buildReturnMono("没有认证",exchange);
}
//判断是否是有效的token
OAuth2AccessToken oAuth2AccessToken;
try {
oAuth2AccessToken = tokenStore.readAccessToken(token); boolean expired = oAuth2AccessToken.isExpired();
if (expired) {
return buildReturnMono("认证令牌已过期",exchange);
}
return chain.filter(exchange);
} catch (InvalidTokenException e) {
log.info("认证令牌无效: {}", token);
return buildReturnMono("认证令牌无效",exchange);
} } /**
* 获取token
*/
private String getToken(ServerWebExchange exchange) {
String tokenStr = exchange.getRequest().getHeaders().getFirst("Authorization");
if (StringUtils.isBlank(tokenStr)) {
return null;
}
String token = tokenStr.split(" ")[1];
if (StringUtils.isBlank(token)) {
return null;
}
return token;
}
private Mono<Void> buildReturnMono(String error, ServerWebExchange exchange) {
ServerHttpResponse response = exchange.getResponse();
String jsonString = JSON.toJSONString(new RestErrorResponse(error));
byte[] bits = jsonString.getBytes(StandardCharsets.UTF_8);
DataBuffer buffer = response.bufferFactory().wrap(bits);
response.setStatusCode(HttpStatus.UNAUTHORIZED);
response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
return response.writeWith(Mono.just(buffer));
} @Override
public int getOrder() {
return 0;
}
}
    // 假设已经从请求中获得了token
String token = getToken(exchange); // 如果token存在且不为空
if (token != null && !token.isEmpty()) {
// 创建一个新的ServerHttpRequest,并添加token到Authorization头
ServerHttpRequest mutatedRequest = exchange.getRequest().mutate()
.header("Authorization", "Bearer " + token)
.build(); // 使用ServerWebExchangeDecorator来包装原始的ServerWebExchange,并且使用修改后的请求
ServerWebExchange mutatedExchange = exchange.mutate()
.request(mutatedRequest)
.build(); return chain.filter(mutatedExchange);
}

3.配置白名单security-whitelist.properties

/media/open/**=媒资管理公开访问接口

4.在其他微服务模块中放行所有路由,并且直接通过SecurityContextHolder.getContext().getAuthentication()获取对象(因为在网关中已经校验完了,这里只负责授权)

module:others

1.包装一个SecurityUtil。SecurityContextHolder.getContext().getAuthentication().getPrincipal();即可获得具体username,然后将username转为实体类即可使用

@Slf4j
public class SecurityUtil { public static XcUser getUser() {
try {
Object principalObj = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (principalObj instanceof String) {
//取出用户身份信息
String principal = principalObj.toString();
//将json转成对象
XcUser user = JSON.parseObject(principal, XcUser.class);
return user;
}
} catch (Exception e) {
log.error("获取当前登录用户身份出错:{}", e.getMessage());
e.printStackTrace();
} return null;
} @Data
public static class XcUser implements Serializable { private static final long serialVersionUID = 1L; private String id; private String username; private String password; private String salt; private String name;
private String nickname;
private String wxUnionid;
private String companyId;
/**
* 头像
*/
private String userpic; private String utype; private LocalDateTime birthday; private String sex; private String email; private String cellphone; private String qq; /**
* 用户状态
*/
private String status; private LocalDateTime createTime; private LocalDateTime updateTime; } }

使用说明:上文使用了成员内部类,XcUser定义在外部类的成员位置,其不能创建独立对象,必须依靠外部类实例.new 内部类()来创建实例

SecurityUtil.XcUser user = SecurityUtil.getUser();

Q&A:

token在gateway中传递下去的?

(无oauth2集成)一般情况下我们需要修改ServerWebExchange里的ServerHttpRequest,但是ServerHttpRequest是不可变的。因此如果我们需要添加上token,就需要新建一个ServerWebExchange,重写ServerHttpRequest。

除此以外,因为我们还会将请求传递给下一个GatewayFilterChain,我们可以在这个请求传递的时候通过修改ServerWebExchange的MutableHttpRequest:

(有oauth2集成)springsecurity的过滤器链会自动处理从请求中提取token,并将其附加到上下文中,下游的过滤器会通过springsecurity的API获取他

为什么其他微服务模块能直接通过SecurityContextHolder.getContext().getAuthentication()获取对象?

在微服务中,每一个模块都有独属于自己的JVM,拥有独属于自己的线程,也就是说每个模块的SecurityContext是不一样的。当我们使用oauth2,用户通过上述gateway进行认证,并获取一个jwt将其放在request中。

当一个模块接收到一个包含oauth2令牌的http请求时,他会使用这个令牌来验证用户身份,并且基于令牌构建一个新的SecurityContext。也就是说SecurityContext的实现其实是局限于单个服务的单个线程的,跨服务的安全信息传递是由oauth2进行的

微服务集成springsecurity实现认证的更多相关文章

  1. ScalaPB(0): 找寻合适的内部系统微服务集成工具

    前一段时间我们探讨了SDP的一个基于集群的综合数据平台解决方案,由多种数据库组成,包括:JDBC, Cassandra 及MongoDB.其中Cassandra和MongoDB属于分布式数据库,可以在 ...

  2. (12)go-micro微服务JWT跨域认证

    目录 一 JWT介绍 二 JWT优缺点 三 JWT使用 1. 导包和数据定义 2.生成JWT 3.解析JWT 4.完整代码 四 最后 一 JWT介绍 JWT 英文名是 Json Web Token , ...

  3. Spring Cloud(一)简单的微服务集成Eureka

    1        Spring Cloud简介 1.1             简介 Spring Cloud项目的官方网址:https://projects.spring.io/spring-clo ...

  4. Spring cloud微服务安全实战-6-2JWT认证之认证服务改造

    首先来解决认证的问题. 1.效率低,每次认证都要去认证服务器调一次服务. 2.传递用户身份,在请求头里面, 3.服务之间传递请求头比较麻烦. jwt令牌. spring提供了工具,帮你在微服务之间传递 ...

  5. 微服务系列之授权认证(一) OAuth 2.0 和 OpenID Connect

    1.传统架构的授权认证 传统应用架构,用户使用账号密码登录后,可以使用前端cookie存储登录状态,也可以使用后端session方式存储登录状态,小应用这么做其实很高效实用,当应用需要横向扩展时,就需 ...

  6. 微服务系列之授权认证(三) JWT

    1.JWT简介 官方定义:JWT是JSON Web Token的缩写,JSON Web Token是一个开放标准(RFC 7519),它定义了一种紧凑的.自包含的方式,可以将各方之间的信息作为JSON ...

  7. Spring Cloud微服务集成配置中心

    1. 搭建Spring Cloud Config配置中心(见上一篇博客) 2. 创建微服务项目bounter-simon-app,pom文件如下: <?xml version="1.0 ...

  8. 微服务系列之授权认证(二) identity server 4

    1.简介 IdentityServer4 是为ASP.NET Core系列量身打造的一款基于 OpenID Connect 和 OAuth 2.0 认证授权框架. 官方文档:https://ident ...

  9. Devops 开发运维高级篇之Jenkins+Docker+SpringCloud微服务持续集成(上)

    Devops 开发运维高级篇之Jenkins+Docker+SpringCloud微服务持续集成(上) Jenkins+Docker+SpringCloud持续集成流程说明 大致流程说明: 1) 开发 ...

  10. Taurus.MVC 微服务框架 入门开发教程:项目集成:1、服务端:注册中心、网关(提供可运行程序下载)。

    系列目录: 本系列分为项目集成.项目部署.架构演进三个方向,后续会根据情况调整文章目录. 本系列第一篇:Taurus.MVC V3.0.3 微服务开源框架发布:让.NET 架构在大并发的演进过程更简单 ...

随机推荐

  1. 来自多彩世界的控制台——C#控制台输出彩色字符画

    引言 看到酷安上有这样一个活动,萌生了用 C# 生成字符画的想法,先放出原图.   酷安手绘牛啤     §1 黑白 将图像转换成字符画在 C# 中很简单,思路大致如下: 加载图像,逐像素提取明度. ...

  2. 谁说爬虫只能Python?看我用C#快速简单实现爬虫开发和演示!

    前言:说到爬虫,基本上清一色的都知道用Python,但是对于一些没玩过或者不想玩Python的来说,却比较头大一点.所以以下我站在C# 的角度,来写一个简单的Demo,用来演示C# 实现的简单小爬虫. ...

  3. ReplayKit2:声音回调时间戳问题

    一.ReplayKit2 框架回调中 视频.micphone声音.系统声音三路回调 - (void)processSampleBuffer:(CMSampleBufferRef)sampleBuffe ...

  4. Java BigInteger类和BigDecimal()类

    BigInteger类 BigInteger 支持任意精度的整数,可以准确的表达任何大小的整数而不丢失精准度 BigInteger 位于 java.math包中 BigInteger()括号里必须是字 ...

  5. 鸿蒙HarmonyOS实战-ArkTS语言基础类库(容器类库)

    前言 容器类库是指一组用于存储和管理数据的数据结构和算法.它们提供了各种不同类型的容器,如数组.链表.树.图等,以及相关的操作和功能,如查找.插入.删除.排序等. 容器类库还可以包含其他数据结构和算法 ...

  6. vue局部注册

    只能在当前注册它的vue实例中使用,通过某个 Vue 实例/组件的实例选项 components 注册仅在其作用域中可用的组件 var Child = { template: '<div> ...

  7. Ubuntu 上使能 SELinux

    首发公号:Rand_cs 此文档说明如何在 ubuntu 上启用 SELinux,测试环境为虚拟机,开始前一定一定一定先来个快照,不要问我为什么有三个一定. 卸载 apparmor(可选) ubunt ...

  8. 剑指Offer-66.机器人的运动范围(C++/Java)

    题目: 地上有一个m行和n列的方格.一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子. 例如,当k为18时,机器人能够 ...

  9. AnkhSVN For Visual Studio 2022

    AnkhSVN For VS2022 AnkhSVN 2.9.87 - Visual Studio 2022 support https://github.com/AmpScm/AnkhSVN/rel ...

  10. EF MYSQL DB FIRST 出现2次数据库名

    环境:使用ADO设计器添加的数据库实体,运行时出现 :Table 'world.world.city' doesn't exist . world 是mysql的演示数据库. MySql.Data.E ...