首页 > 其他分享 >SpringSecurity与JWT如何实现项目端分离认证与授权

SpringSecurity与JWT如何实现项目端分离认证与授权

时间:2024-02-20 19:55:19浏览次数:29  
标签:SpringSecurity 登录 Spring JWT 用户 认证 Security 权限

✅SpringSecurity+JWT 实现项目前端分离认证授权✅


1. 简介

Spring Security是Spring家族中的一个安全管理框架。相比与另外一个安全框架Shiro,它提供了更丰富的功能,杜区资源也比Shiro丰富。

一般来说中大型的项目都是使用Spring Security来做安全框架。小项目有Shiro的比较多,因为相比与Spring Security,Shiro的上手更加的简单,

一般Web应用的需要进行认证和授权,
认证:验证当前访问系统的是不是本系统的用户,并且要确认具体是哪个用户
授权:经过认证后判断当前用户是否有权限进行某个操作
而认证和授权也是Spring Security作为安全框架的核心功能。

没有引入Spring Security框架之前访问一个网页是直接访问的

Untitled

加入了Spring Security之后,默认跳转到登陆页面

Untitled

这个实际上就是一个认证的过程,登录成功之后才能够访问默认的接口

Untitled

是 有一个默认的退出接口的, logout

2. 登录校验流程

Untitled

前后端沟通使用token,判断是否携带对应的Token来判断是否是系统的用户,也可以拿到Token获取到加密之前的一些数据,用来判断是哪一用户

登录的时候

  1. 前端发送用户名密码给服务器进行一个比对,如果说一切的信息都是正确的,服务器后将一些用户信息比如说用户名,用户密码,创建时间生成一个JWT,然后将这个生成的JWT Token响应给前端,前端进行一个存储(Local Storage)
  2. 然后前端再去访问其他请求的时候,都会在请求头中携带Token数据,然后服务器可以获取请求头中的Token进行解析获取到Token中的信息然后进行比对,如果解析成功就可以获取到之前一些加密的数据比如说UserID, 密码之类的
  3. 拿到UserID之后就可以获取用户的其他信息,比如说用户的权限,能否访问对应的接口等等。

image-20211214151515385.png

  1. 接收用户凭据:当用户尝试登录时,他们的凭据(通常是用户名和密码)会被UsernamePasswordAuthenticationFilter接收,这是AbstractAuthenticationProcessingFilter的一个具体实现。
  2. 生成Authentication对象:过滤器将基于用户提供的信息创建一个未经认证的Authentication对象。
  3. AuthenticationManager进行认证:这个未经认证的Authentication对象会被传递给AuthenticationManager,具体是它的实现类ProviderManager,来进行验证。
  4. AuthenticationProvider进行认证ProviderManager会调用DaoAuthenticationProvider,它是AbstractUserDetailsAuthenticationProvider的一个实现。
  5. 加载用户详情DaoAuthenticationProvider会使用UserDetailsService接口来获取用户详情,这里具体使用了InMemoryUserDetailsManager实现。
  6. UserDetailsService返回用户信息UserDetailsService会加载用户的详细信息并返回一个UserDetails对象。
  7. 密码比对DaoAuthenticationProvider会使用PasswordEncoder来比对提交的密码和UserDetails中存储的密码。
  8. 构建经过认证的Authentication对象:如果用户凭据有效,AuthenticationProvider会构建一个包含用户权限的已认证Authentication对象。
  9. 保存Authentication对象:成功认证后,认证对象会被存入SecurityContext中,以供后续请求使用。
  10. 更新SecurityContext:最后,SecurityContextHolder的上下文会被更新,以包含新的认证信息。

3. SpringSecurity实现流程

原理上其实就是一个过滤器链,内部包含了各种功能的过滤器

  1. UsernamePasswordAuthentiationFilter : 负责处理在登录页面填写的用户名密码请求登录。
  2. ExceptionTranslationFilter: 认证授权这个流程出现的异常都会被这个过滤器所捕获。
  3. FilterSecurityInterceptor : 这个过滤器主要是判断授权的功能。

4. 解决方案

Untitled

登录

1️⃣ 自定义UserDetailService , 在这个实现类中去查询数据库

2️⃣ 自定义登录接口,调用ProviderManager的方法进行认证,如果生成通过生成JWT,把用户信息存入Redis中

校验

1️⃣ 定义基于JWT的认证过滤器

1️⃣ 获取Token

2️⃣ 解析Token,获取其中的UserID

3️⃣ 从Redis中获取用户信息

4️⃣ 存入SecurityContextHolder

Untitled

5. Spring Security密码加密存储

Spring Security 明文存储前面加 {noop} 采用默认加密

实际项目中不会吧密码明文存储到数据库中。

默认使用的PasswordEncoder 要求数据库的密码格式为:{id}password。它会根据id去判断密码的加密方式,但是一般不会采用这种方式,所以就需要替换PasswordEncoder.

一般使用Spring Security中的BCryptPasswordEncoder

Untitled

在使用BCyptPasswordEncoder加密的时候,虽然加密的原文是一致的,但是由于每次生成的盐值不同,所以每次产生的密文也不相同

BCyptPasswordEncoder加密格式为: $2a$10$ + 盐(22位) + 密文

6. 登录接口

自定义登录接口的话肯定是要定义Controller

对于登录接口肯定是要Spring Security进行放行操作的。需要在对应的Spring Security配置类里面进行相应的配置,重写configure方法

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
        //关闭csrf
        .csrf().disable()
        //不通过Session获取SecurityContext
        .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
        .and()
        .authorizeRequests()
        // 对于登录接口 允许匿名访问
        .antMatchers("/user/login").anonymous()
        // 除上面外的所有请求全部需要鉴权认证
        .anyRequest().authenticated();
}

实际上的操作实在Service当中

 		/**
     * 用户登录
     * @param user
     * @return
     */
    @Override
    public ResponseResult login(User user) {

//        AuthenticationManager 进行用户认证
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(user.getUserName(), user.getPassword());
        Authentication authenticate = authenticationManager.authenticate(authenticationToken);

//        如果认证没有通过,给出对应的提示
        if(Objects.isNull(authenticate)){
           throw new RuntimeException("登录失败");
        }

//        如果认证通过了,使用UserID生成一个JWT JWT存入ResponseResult进行返回
        LoginUser loginUser = (LoginUser) authenticate.getPrincipal();
        String userId = loginUser.getUser().getId().toString();
        String jwt = JwtUtil.createJWT(userId);
        Map<String, String> map = new HashMap<>();
        map.put("token", jwt);

//        把完整的用户信息存入Redis userId作为key
        redisCache.setCacheObject("login:" + userId, loginUser);
        return new ResponseResult(200, "登录成功", map);
    }

7. 退出登录

退出登录实际上就是携带之前的Token登录访问,现在的用户相当于是一个未认证的状态,需要重新登录,可以将SecurityContextHolder中的对象删除掉

当用户携带着他的Token访问注销接口的时候,服务器会获取到SecurityContextHolder中的userId, 然后在Redis中删除对应的用户信息,当用户再次携带之前的Token访问其他服务的时候,会被第一个JwtToken拦截器解析出Token中的用户ID,在Redis中查询不到,抛出异常,授权不成功

8. 授权

在Spring Security中,会使用默认的FilterSecurityInterceptor类进行权限校验。在FilterSecirutyInterceptor中会从SecurityContextHolder中获取其中的Authentication, 然后获取其中的信息, 当前用户是否拥有访问当前资源所需的权限。

所以在项目中只需要把当前登录用户的权限信息也存入Authentication

  1. 将用户的权限信息封装到Authentication这个对象当中
  2. 设置对应接口的一些访问权限
  3. 实际的权限信息应该从数据库进行一个查询

Untitled

关于这个注解判断用户是否有hasAuthority()括号里面的权限,实际上是执行下面的这个方法,用户必须拥有sys:user:info这个权限才能够访问这个接口

Untitled

8. 权限

RBAC权限模型(Role-Based Access Control) 基于角色的权限控制。这是目前最常杯卡法这使用也是相对容易,通用的权限模型。

一个用户可以有多个权限,但是如果只是用用户表以及权限表的话就会有一个问题,当有一个复杂的系统用户肯定是非常的多,那给每一个用户设置权限的时候就会显得非常的麻烦,这个权限粒度是非常小的,所以一个一个配置的话就会非常的麻烦,甚至产生数据冗余的问题。所以可以一次性配置一组权限信息,一系列的权限信息直接配置给用户,所以引入了一个角色的概念

  1. 用户表——user
  2. 权限表——menu
  3. 角色表(权限组)——role

权限和角色是有关联的。一个角色是可以具有多个权限的,同时一个权限是可以对应多个角色,其中是一个多对多的关系。需要一个角色权限关联表(role_menu)

  1. 角色权限关联表——role_menu

同理,用户表和角色表也是多对多的关系,这也需要一个关联表: 用户角色关联表(user-role)

  1. 用户角色关联表——user_role
SELECT DISTINCT
	m.perms 
FROM
	sys_user_role ur
	LEFT JOIN sys_role r ON ur.role_id = r.id
	LEFT JOIN sys_role_menu rm ON ur.role_id = rm.role_id
	LEFT JOIN sys_menu m ON m.id = rm.menu_id 
WHERE
	user_id = 1 
	AND r.`status` = 0 
	AND m.`status` = 0

标签:SpringSecurity,登录,Spring,JWT,用户,认证,Security,权限
From: https://www.cnblogs.com/pronting/p/18023954

相关文章

  • c-jwt-cracker Jwt 密钥爆破工具的安装使用
    c-jwt-cracker  项目地址:https://github.com/brendan-rius/c-jwt-cracker BuildaDockerImagedockerbuild.-tjwtcrack RunonDocker  docker版使用方法dockerrun-it--rmjwtcrackeyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODk......
  • ssl 认证
    http和https区别-http:超文本传输协议-https:安全的超文本传输协议-https=http+ssl/tls-防止:篡改,截取-必须有证书:才能通信使用方式importrequestsheader={'User-Agent':'Mozilla/5.0(WindowsNT10.0;Win64;x64)AppleWebKit/537.36(KHTML,likeGecko)......
  • djangorestframework-simplejwt 的使用
    djangorestframework-simplejwt使用转载于:https://www.cnblogs.com/liuqingzheng/p/179422271快速使用1.1配置#1安装pipinstalldjangorestframework-simplejwt#2路由层fromrest_framework_simplejwt.viewsimporttoken_obtain_pair,token_verify,token_refre......
  • Java21 + SpringBoot3使用Spring Security时如何在子线程中获取到认证信息
    目录前言原因分析解决方案方案1:手动设置线程中的认证信息方案2:使用DelegatingSecurityContextRunnable创建线程方案3:修改SpringSecurity安全策略通过设置JVM参数修改安全策略通过SecurityContextHolder修改安全策略总结前言近日心血来潮想做一个开源项目,目标是做一款可以适配多......
  • 【认证】验证确认实体的身份真实性、合法性
     认证是确认某个实体(如个人、设备或数据)的身份真实性和合法性的过程。在身份验证中,个人必须提供一些证据或信息(密钥)来证明他们所声称的身份是正确的。 签名与验证是数字认证的一种常见方式。在这种情况下,使用加密算法生成一个数字签名,并将其附加到数据上。数字签名可以确保数......
  • JwtAuthenticationTokenFilter
    packagecom.oep.backend.config.filter;//实现config.filter.JwtAuthenticationTokenFilter类,用来验证jwttoken,如果验证成功,则将User信息注入上下文中importcom.oep.backend.mapper.AccountMapper;importcom.oep.backend.pojo.Account;importcom.oep.backend.service......
  • src.backend.utils.JwtUtil
    packagecom.oep.backend.utils;importio.jsonwebtoken.Claims;importio.jsonwebtoken.JwtBuilder;importio.jsonwebtoken.Jwts;importio.jsonwebtoken.security.SecureDigestAlgorithm;importorg.springframework.stereotype.Component;importjavax.crypto.Sec......
  • SpringSecurity
    1、项目依赖<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId><version>2.7.14</version></dependency><dependency><groupId>......
  • 红帽认证有几种?考完工资多少?
    计算机技能已经成为了当今职场的必备要求。在众多认证中,红帽认证以其高含金量和广泛的应用领域受到了众多求职者的青睐。那么,红帽认证到底有几种?考完红帽认证的工资又是多少呢?下面将为你一一解答。01红帽认证有几种?红帽认证的种类,红帽认证主要包括以下几种:RHCSA英文全称:RedHatCe......
  • 网工内推 | 高级网工,IE认证优先,最高15K,五险一金
    01丰沃创新(北京)科技有限公司招聘岗位:高级网络工程师职责描述:1.主要负责移动营运商数据中心机房网络的维护工作;2.负责防火墙策略调整,负责交换机路由器等网络设备的配置;3.负责云专线的入网配置;4.负责处理网络突发状况,例如卡顿、环路、网络通讯断开等问题;5.其他项目经理安排的......