Java集成Sa-Token进行认证与授权

引言

        软件开发过程中都必须要有的一个功能,那就是认证与授权,经过大佬们的不断更新迭代,使得如今实现认证与授权功能变得相对简单,也许你并不能真正的接触到认证与授权这一功能,除非你接触的项目是从0到1的,即便是这样的项目,认证与授权这一块依旧是系统架构师负责的,正因如此,我们更应该去学习认证与授权,扩展自己的知识面,这样才能在未来成为别人眼中的大佬,那么什么是认证与授权呢,简单讲就是在进入系统前需要进行账号登录,登录时系统根据用户输入的账号密码进行验证,验证通过后返回该用户所拥有的操作权限,本文会从Java认证与授权的发展历程和现在使用最多的标准和框架来讲述

发展历程

        Java程序中的认证与授权机制经历了从简单到复杂、从单一到多样化的历史进程

早期

        校验用户名和密码,但随着网络攻击手段的不断迭代,这种方式就暴露除了缺陷

JavaSE时期

           JavaSE平台提供了更加灵活和安全的认证与授权解决方案,Java Authentication and AuthorizationService(标准认证与授权API),允许开发者在运行时动态配置应用程序的安全策略,支持多种认证方式,

JAVAEE时期

        Java应用开始支持更加复杂的认证与授权机制

Spring Security时期

        随着Spring框架的流行,其生态中的Spring Security逐渐成为Java应用中最受欢迎的认证与授权框架之一,它提供了全面的认证与授权功能,支持多种认证与机制,还提供了基于角色的访问控制(RBAC)、基于属性的访问控制(ABAC)以及基于策略的访问控制(PBAC)等,使得开发者能够更加方便地实现复杂地访问控制逻辑

OAuth2与OpenConnect

        随着第三方应用和服务的兴起,OAuth2和OpenID Connect逐渐成为Java应用中处理第三方认证与授权的重要标准。OAuth2运行第三方应用获取用户在HTTP服务上的部分信息,而无需获取用户的密码和访问所有数据。OpenID Connect则是一种基于OAuth2的身份验证协议,它允许用户通过第三方身份提供者进行身份验证,并且获取有关用户的身份信息。Java中,Spring Security OAuth项目为开发者提供了实现OAuth2和OpenID Connect认证与授权的必要API和工具,这使得Java应用能够更加方便地集成第三方认证服务,实现用户在不同应用程序间地无缝登录和权限管理

现在

        经过长时间地发展,现在Java应用中的认证与授权机制已经变得更加复杂和多样化。除了传统的校验用户名/密码和基于角色的访问控制等外,还涌现出了许多新的认证方式和授权策略,如基于令牌的身份验证(JWT),多因素认证(MFA)等,这些都使得Java应用的安全保护能力更加灵活和强大。并且随着云计算、微服务和容器技术的普及,Java应用中的认证与授权机制也开始向分布式、可扩展和自动化的方向发展,如,Spring Cloud Security,使得开发者可以更加方便地实现微服务之间地认证与授权集成,以及基于策略的自动化访问控制

        随着技术的不断进步和应用场景的不断拓展,Java应用中的认证与授权机制将继续向更加安全、灵活和智能化的方向发展

使用最多的标准和框架

标准

        OAuth 2.0

                开放标准,允许用户提供一个令牌,而不是用户名和密码,给第三方应用以访问它们其他服务上存储的私密资源,被广泛用于API授权,使得第三方应用能够安全的访问用户资源,通常结合Spring Security OAuth2等扩展库使用,实现授权服务器的搭建和客户端的授权请求

        OpenID Connect

                OAuth 2.0的扩展层,它允许用户通过单一的身份标识进行身份验证,并获取关于该用户的信息,在OAuth 2.0的认证流程基础上新增了用户信息的获取功能,广泛应用在Java应用中,特别时单点登录功能

        JWT

                一种用于双方安全传输信息的简洁的、URL安全的令牌标准,基于JSON对象,通过数字签名的方式进行验证和确保信息的安全性,通常与Spring Security等安全框架结合使用,用于生成、解析和校验令牌,广泛应用在分布式系统实现单点登录等功能

框架

        Spring Security

                Spring生态中的一个安全框架,提供了全面的安全性解决方案,支持认证、授权、加密、会话管理等安全功能,并且与Spring生态系统中的其他组件无缝集成,其中认证方式有多种:表单登录、HTTP Basic认证、OAuth2等,并且支持基于角色的访问控制和基于属性的访问控制

        Apache Shiro

                一个轻量级的Java安全框架,提供了身份验证、授权、加密和会话管理功能,还提供了丰富的配置选项和文档支持,适用于小型项目或对性能要求较高的场景

Sa-Token

简介

        最近几年出现的轻量级Java权限认证框架,主要解决:登录认证、权限认证、单点登录、OAuth 2.0、分布式Session会话、微服务网关鉴权等一系列权限相关问题,它的成功得益于简单且直观的API设计、模块化设计易于集成、对性能进行优化同时提供高效的加密算法来提高安全性、提供了丰富的扩展接口等,被广泛应用于Web应用中,因为小编最近有使用,感觉不错,推荐给大家

集成与使用

        以微服务为例讲解两种不同环境的集成,网关采用Spring WebFlux环境,其他服务采用SpringBoot环境

Spring WebFlux环境

Maven依赖
<!-- Sa-Token 权限认证(Reactor响应式集成) -->
<dependency>
    <groupId>cn.dev33</groupId>
    <artifactId>sa-token-reactor-spring-boot-starter</artifactId>
    <version>1.39.0</version>
</dependency>
配置全局过滤器
/**
 * [Sa-Token 权限认证] 全局配置类
 * @author muze
 */
@Configuration
public class SaTokenConfig {
    /**
     * 注册 [Sa-Token全局过滤器]
     */
    @Bean
    public SaReactorFilter getSaReactorFilter() {
        return new SaReactorFilter()
                // 指定 [拦截路由]
                .addInclude("/**")
                // 指定 [放行路由]
                .addExclude("/login")
                // 指定[认证函数]: 每次请求执行登录校验
                .setAuth(obj -> StpUtil.checkLogin())
                // 指定[异常处理函数]:每次[认证函数]发生异常时执行此函数
                .setError(e -> SaResult.error(e.getMessage()));
    }
}
跨域解决方式
一、在全局过滤器中链式调用如下方法
                // 前置函数:在每次认证函数之前执行
                .setBeforeAuth(obj -> {
                    SaHolder.getResponse()
                            // ---------- 设置跨域响应头 ----------
                            // 允许指定域访问跨域资源
                            .setHeader("Access-Control-Allow-Origin", "*")
                            // 允许所有请求方式
                            .setHeader("Access-Control-Allow-Methods", "*")
                            // 允许的header参数
                            .setHeader("Access-Control-Allow-Headers", "*")
                            // 有效时间
                            .setHeader("Access-Control-Max-Age", "3600");
                    // 如果是预检请求,则立即返回到前端
                    SaRouter.match(SaHttpMethod.OPTIONS)
                            .free(r -> System.out.println("--------OPTIONS预检请求,不做处理"))
                            .back();
                });
二、自定义WebFlux配置
/**
 * 自定义WebFlux配置
 * 实现WebFlux接口
 * 覆写配置 CORS(跨源资源共享)策略方法
 * @author muze
 */
@Configuration
public class CustomWebFluxConfig implements WebFluxConfigurer {
    /**
     * 配置 CORS(跨源资源共享)策略
     * @param corsRegistry 配置 CORS(跨源资源共享)策略的注册器
     */
    @Override
    public void addCorsMappings(CorsRegistry corsRegistry) {
        corsRegistry.addMapping("/**") // 允许跨域的路径
                .allowedOrigins("*") // 允许跨域请求的域名
                .allowedMethods("GET", "POST", "PUT", "DELETE") // 允许的请求方法
                .allowedHeaders("*") // 允许的请求头
                .allowCredentials(true); // 允许证书(cookies)
    }
}

SpringBoot环境

Maven依赖
<!-- Sa-Token 权限认证 -->
<dependency>
    <groupId>cn.dev33</groupId>
    <artifactId>sa-token-spring-boot-starter</artifactId>
    <version>1.39.0</version>
</dependency>
配置yml文件
############## Sa-Token 配置 ##############
sa-token: 
    # token 名称(同时也是 cookie 名称)
    token-name: satoken
    # token 有效期(单位:秒) 默认30天,-1 代表永久有效
    timeout: 2592000
    # token 最低活跃频率(单位:秒),如果 token 超过此时间没有访问系统就会被冻结,默认-1 代表不限制,永不冻结
    active-timeout: -1
    # 是否允许同一账号多地同时登录 (为 true 时允许一起登录, 为 false 时新登录挤掉旧登录)
    is-concurrent: true
    # 在多人登录同一账号时,是否共用一个 token (为 true 时所有登录共用一个 token, 为 false 时每次登录新建一个 token)
    is-share: true
    # token 风格(默认可取值:uuid、simple-uuid、random-32、random-64、random-128、tik)
    token-style: uuid
    # 是否输出操作日志 
    is-log: true

集成好后我们只需要在登录业务中调用Sa-Token提供的登录API:login,传入用户主键即可

/**
 * 用户控制层
 * @author muze
 */
@RestController
public class UserController {
    /**
     * 登录
     * @param username 用户名
     * @param password 密码
     * @return 登录结果
     */
    @PostMapping("/login")
    public SaResult login(String username, String password) {
        // TODO:校验用户名和密码
        // 正确则调用login传入用户主键,返回登录结果
        StpUtil.login(1);
        return SaResult.ok("登录成功");
    }
}

这样就登录成功了,Sa-Token会生成一个令牌并保存到浏览器的cookie中,后续请求前端需要携带cookie中的令牌访问后端接口,网关会在全局过滤器中调用checkLogin去校验是否已经登录

如果我们的项目没有网关,那么我们就需要配置跨域和拦截器,每次请求在拦截器中进行登录验证

/**
 * 跨域配置
 * @author muze
 */
@Configuration
public class WebConfig implements WebMvcConfigurer {
    /**
     * 添加跨域映射
     * @param registry Cross-Origin Resource Sharing Registry:跨域资源共享登记
     */
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        // 允许跨域访问的URL模式
        registry.addMapping("/**")
            // 允许跨域请求的域名(ip:port)
            .allowedOrigins("*")
            // 允许的方法
            .allowedMethods("GET", "POST", "PUT", "DELETE")
            // 允许的头信息
            .allowedHeaders("*")
            // 是否允许发送Cookie
            .allowCredentials(false);
    }
    /**
     * 添加拦截器
     * @param interceptorRegistry 注册拦截器实体
     */
    @Override
    public void addInterceptors(InterceptorRegistry interceptorRegistry) {
        //添加 Sa-Token 拦截器,校验规则为 登录校验 StpUtil.checkLogin()
        interceptorRegistry.addInterceptor(new SaInterceptor(handle -> StpUtil.checkLogin()))
                // 添加校验路径
                .addPathPatterns("/**")
                // 排除校验路径
                .excludePathPatterns("/login");
    }
}

到这里,一个Java集成Sa-Token并完成一个简单的登录和登录验证就完成了,还有很多简单好用的API,详情请参考官方在线文档:Sa-Token

总结

        当我们在职场中还接触不到认证与授权,但又希望了解并学习来提升自己和为未来做准备的话,可以进行自主学习,该篇文章就是小编的自主学习并使用的一些心得,希望能帮助到你们,感兴趣的朋友赶紧去试试吧,相信这篇文章能给你们一些帮助

上一篇:嵌入式 FPGA开发


下一篇:单例模式(Singleton Pattern):深入解析与应用场景