首页 星云 工具 资源 星选 资讯 热门工具
:

PDF转图片 完全免费 小红书视频下载 无水印 抖音视频下载 无水印 数字星空

微服务集成springsecurity实现认证

编程知识
2024年07月27日 22:03

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进行的

From:https://www.cnblogs.com/kun1790051360/p/18324363
本文地址: http://shuzixingkong.net/article/500
0评论
提交 加载更多评论
其他文章 《最新出炉》系列入门篇-Python+Playwright自动化测试-55- 上传文件 (非input控件)- 中篇
1.简介 在实际工作中,我们进行web自动化的时候,文件上传是很常见的操作,例如上传用户头像,上传身份证信息等。所以宏哥打算按上传文件的分类对其进行一下讲解和分享。 2.上传文件的API(非input控件) Playwright是一个现代化的自动化测试工具,它支持多种浏览器和操作系统,可以帮助开发人
《最新出炉》系列入门篇-Python+Playwright自动化测试-55- 上传文件 (非input控件)- 中篇 《最新出炉》系列入门篇-Python+Playwright自动化测试-55- 上传文件 (非input控件)- 中篇 《最新出炉》系列入门篇-Python+Playwright自动化测试-55- 上传文件 (非input控件)- 中篇
手把手教你集成GraphRag.Net:打造智能图谱搜索系统
在人工智能和大数据发展的背景下,我们常常需要在项目中实现知识图谱的应用,以便快速、准确地检索和使用信息。 今天,我将向大家详细介绍如何在一个新的.NET项目中集成GraphRag.Net,这是一个参考GraphRag实现的.NET版本,能够实现图谱数据的存储、检索、和问答功能。 在此之前,如果你还不
手把手教你集成GraphRag.Net:打造智能图谱搜索系统 手把手教你集成GraphRag.Net:打造智能图谱搜索系统
Python 实现行为驱动开发 (BDD) 自动化测试详解
​ 在当今的软件开发领域,行为驱动开发(Behavior Driven Development,BDD)作为一种新兴的测试方法,逐渐受到越来越多开发者的关注和青睐。Python作为一门功能强大且易于使用的编程语言,在实现BDD方面也有着独特的优势。那么,如何利用Python实现BDD自动化测试呢?本
Python 实现行为驱动开发 (BDD) 自动化测试详解 Python 实现行为驱动开发 (BDD) 自动化测试详解 Python 实现行为驱动开发 (BDD) 自动化测试详解
利用Elasticsearch实现地理位置、城市搜索服务
最近用到一些简单的地理位置查询接口,基于当前定位获取用户所在位置信息(省市区),然后基于该信息查询当前区域的......提供服务。 然后就自己研究了下GIS,作为一个程序员。自己能不能实现这个功能呢?答案当然是可以。立即开干。 思路:找到数据,写入数据库,利用Elasticsearch强大的搜索能力
利用Elasticsearch实现地理位置、城市搜索服务 利用Elasticsearch实现地理位置、城市搜索服务
APK包的加固手段收集(浅)
目录防止APK被调试加壳代码混淆:检测调试器:使用反调试技术:环境检测:使用Native代码:多层防护:防止APK被篡改签名校验:V1 签名机制V2 签名机制V3 签名机制动态完整性校验: 想要保护APK应用内的代码逻辑安全,需要做一定的APK加固操作。加固的常见操作有: 混淆 加密 隐藏 检测 .
[rCore学习笔记 018]实现特权级的切换
写在前面 本随笔是非常菜的菜鸡写的。如有问题请及时提出。 可以联系:1160712160@qq.com GitHhub:https://github.com/WindDevil (目前啥也没有 本节内容 因为risc-v存在硬件特权级机制,我们又要实现一个可以使得应用程序工作在用户级,使得操作系统工
[rCore学习笔记 018]实现特权级的切换 [rCore学习笔记 018]实现特权级的切换 [rCore学习笔记 018]实现特权级的切换
如何让SQL Server像MySQL一样拥有慢查询日志(Slow Query Log慢日志)
如何让SQL Server像MySQL一样拥有慢查询日志(Slow Query Log慢日志) SQL Server一直以来被人诟病的一个问题是缺少了像MySQL的慢日志功能,程序员和运维无法知道数据库过去历史的慢查询语句。 因为SQLServer默认是不捕获过去历史的长时间阻塞的SQL语句,导致大
如何让SQL Server像MySQL一样拥有慢查询日志(Slow Query Log慢日志) 如何让SQL Server像MySQL一样拥有慢查询日志(Slow Query Log慢日志) 如何让SQL Server像MySQL一样拥有慢查询日志(Slow Query Log慢日志)
c语言模拟Python的命名参数
最近在书里看到的,让c语言去模拟其他语言里有的命名函数参数。觉得比较有意思所以记录一下。 目标 众所周知c语言里是没有命名函数参数这种东西的,形式参数虽然有自己的名字,但传递的时候并不能通过这个名字来指定参数的值。 而支持命名参数的语言,比如python里,我们能让代码达到这种效果: def k_f