springSecurity(二):实现登入获取token与解析token

news2024/12/23 14:02:27

登入生成token

主要思想

  1. springSecurity使用UsernamePasswordAuthenticationToken类来封装用户名和密码的认证信息

代码实现

发起登入请求后,进入到login()方法

  /**
   * 在接口中我们通过AuthenticationManager的authenticate方法来进行用户认证,
   * 所以需要在SecurityConfig中配置把AuthenticationManager注入容器。
   * @param user
   * @return
   */
  @Override
  public ResponseResult login(User user) {
    //通过AuthenticationManager的authenticate方法来进行用户认证
    UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(user.getUserName(),user.getPassword());
    Authentication authenticate = authenticationManager.authenticate(token);

    //如果没通过,给出对应的提示
    if(Objects.isNull(authenticate)){
      throw new RuntimeException("用户名或密码错误");
    }

    //如果通过了,使用userId生成一个jwt,存入ResponseResult返回
    LoginUser loginUser = (LoginUser)authenticate.getPrincipal();
    String id = loginUser.getUser().getId().toString();
    
    //根据用户id生成token
    String jwt = JwtUtil.createJWT(id);

    //把完整的用户信息存入redis,使用userId作为key
    redisCache.setCacheObject("login:"+id,loginUser);

    ResponseResult responseResult = new ResponseResult();
    responseResult.setCode(200);
    responseResult.setMsg("登入成功");
    Map<String,String> map = new HashMap<>();
    map.put("token",jwt);
    responseResult.setData(map);

    return responseResult;
  }


在执行 Authentication authenticate = authenticationManager.authenticate(token);时,会调用security框架的loadUserByUsername方法查询用户信息,这里我们重写loadUserByUsername方法

@Service
public class UserDetailsServiceImpl implements UserDetailsService {

  @Autowired
  private UserMapper userMapper;

  @Autowired
  private MenuMapper menuMapper;

  @Override
  public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

    //查询用户信息
    LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
    lambdaQueryWrapper.eq(User::getUserName,username);
    User user = userMapper.selectOne(lambdaQueryWrapper);

    //如果没有查询到用户就抛出异常
    if (Objects.isNull(user)){
      throw new RuntimeException("用户名或密码错误");
    }

    //查询对应的权限信息
    List<String> list = menuMapper.selectPermsByUserId(user.getId());

    //把数据封装成UserDetails返回
    return new LoginUser(user,list);
  }
}

这样执行完loadUserByUsername方法之后,就把权限信息封装到LoginUser中

效果展示

调用接口之后返回一个token信息

{
    "code": 200,
    "msg": "登入成功",
    "data": {
        "token": "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiJiODhkZmIxZjExMDg0NmZhOTJlN2I5OTM4M2Q3ZTQ5NyIsInN1YiI6IjQiLCJpc3MiOiJ3aHMiLCJpYXQiOjE3MTg2MjQyMTAsImV4cCI6MTcxODYyNzgxMH0.Z-qa-1rD6dIOxKSaeZ_wjHFKs_TY31hFgZnl2Yld4M4"
    }
}

题外话

看以下UsernamePasswordAuthenticationToken源码的实现思想

public class UsernamePasswordAuthenticationToken extends AbstractAuthenticationToken {
    
}

public abstract class AbstractAuthenticationToken implements Authentication, CredentialsContainer {
    
}

这段代码同时可以参考ArrayList的实现思想

public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable{ 
}


public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
}

思想理论基础

  • 抽象类是一种特殊的类,它的方法体包含抽象方法和非抽象方法
    • 抽象方法是一种没有具体实现的方法,必须在子类被重写实现
    • 非抽象方法是有具体实现的方法,可以在子类中被继承使用
    • 抽象类的主要目的是为了被继承和重写,而不是被实例化
  • 当一个类继承了一个抽象类和实现了一个接口之后,如果抽象类和接口都有方法A(),这个类是否需要实现方法A()呢?
    • 答案是不需要。因为当抽象类和接口中含有相同的方法名、参数列表和返回类型时候,这个类并不需要强制重写这个方法。
    • 接口默认方法和抽象类中的抽象方法是不同的概念,它们可以共存而不会产生冲突

优点

  1. extends抽象类提供了基础实现
    抽象类可以包含部分已实现的方法和成员变量,使子类能够继承这些实现,减少重复代码。例如:
abstract class Animal {
    String name;
    void sleep() {
        System.out.println(name + " is sleeping.");
    }
    abstract void makeSound();
}

  1. implements接口提供了灵活性
    接口定义了一组方法,子类必须实现这些方法。这强制子类提供具体实现,同时接口可以用于实现多重继承,因为一个类可以实现多个接口。例如:
interface Pet {
    void play();
}
  1. 组合使用的话:
    抽象类提供基础功能,接口提供额外功能
public class Dog extends Animal implements Pet {
    public Dog(String name) {
        this.name = name;
    }
    @Override
    void makeSound() {
        System.out.println(name + " says: Woof!");
    }
    @Override
    public void play() {
        System.out.println(name + " is playing.");
    }
}

  1. 灵活的接口实现
    通过实现接口,一个类可以被视为实现该接口的对象类型,这使得代码更加灵活和松耦合
public class Main {
    public static void main(String[] args) {
        Dog dog = new Dog("Buddy");
        dog.sleep(); // Animal类的方法
        dog.makeSound(); // Dog类实现的抽象方法
        dog.play(); // Pet接口的方法

        Pet pet = dog; // 可以用接口类型引用对象
        pet.play(); // 接口的方法
    }
}
  1. 设计模式的支持
    这种组合模式在策略模式和模板方法模式得到了广泛应用。抽象类可以提供通用的算法结构,接口可以定义可替换的行为。

总结

  • 代码复用:继承抽象类可以重用父类的代码。
  • 灵活性:实现接口可以实现多重继承,提供额外功能。
  • 松耦合:接口使得代码更加灵活和易于扩展。
  • 支持设计模式:这种组合在很多设计模式中被广泛应用,提高代码的可维护性和可扩展性。

请求解析token

前端登入获取token之后,之后其他请求在访问接口时候都需要带上token

主要思想

  • 在SpringSecurity中,会使用默认的FilterSecurityInterceptor来进行权限校验。在FilterSecurityInterceptor中会从SecurityContextHolder获取其中的Authentication,然后获取其中的权限信息。当前用户是否拥有访问当前资源所需的权限。
  • 所以我们在项目中只需要把当前登录用户的权限信息也存入Authentication。然后设置我们的资源所需要的权限即可。

代码实现

开启配置

在SecurityConfig类上加上如下注解

@EnableGlobalMethodSecurity(prePostEnabled = true)

在接口上加@PreAuthorize注解

  /**
   * 测试
   * @return
   */
  @RequestMapping("/hello")
  @PreAuthorize("ex.hasAuthority('sysytem:dept:list')")
  public String hello(){
    return "hello";
  }

重写认证过滤器,解析token

@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {

  @Autowired
  private RedisCache redisCache;

  @Override
  protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
    //获取token
    String token = request.getHeader("token");
    if(!StringUtils.hasText(token)){
      //放行
      filterChain.doFilter(request,response);
      return;
    }

    //解析token
    String userId;
    try {
      Claims claims = JwtUtil.parseJWT(token);
      userId= claims.getSubject();
    } catch (Exception e) {
      throw new RuntimeException("token非法");
    }

    //从redis中获取用户信息
    String redisKey = "login:"+userId;
    LoginUser loginUser = redisCache.getCacheObject(redisKey);
    if(Objects.isNull(loginUser)){
      throw new RuntimeException("用户未登入");
    }

    //存入SecurityContextHolder
    //获取权限信息,封装到Authentication中
    UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser,null,loginUser.getAuthorities());
    SecurityContextHolder.getContext().setAuthentication(authenticationToken);

    //放行
    filterChain.doFilter(request,response);

  }
}

效果展示

在header加上token之后可以请求到正确结果
image.png
如果不加token,则会报错401
image.png

后台回复springSecurity获取项目完整代码

搜索框传播样式-白色版

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1847540.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Polyp-DDPM: Diffusion-Based Semantic Polyp Synthesis for Enhanced Segmentation

Polyp- ddpm:基于扩散的语义Polyp合成增强分割 摘要&#xff1a; 本研究介绍了一种基于扩散的方法Polyp-DDPM&#xff0c;该方法用于生成假面条件下息肉的逼真图像&#xff0c;旨在增强胃肠道息肉的分割。我们的方法解决了与医学图像相关的数据限制、高注释成本和隐私问题的挑…

网络编程(四)wireshark基本使用 TCP的三次握手和四次回挥手 TCP和UDP的比较

一、使用wireshark抓包分析协议头 &#xff08;一&#xff09;wireshark常用的过滤语句 tcp.port <想要查看的端口号> ip.src <想要查看的源IP地址> ip.dest <想要查看的目的IP地址> ip.addr <想要查看的IP地址>&#xff08;二&#xff09;抓包分…

DevEco鸿蒙开发请求网络交互设置

首先&#xff0c;在鸿蒙项目下config.json中找到module项&#xff0c;在里面填写"reqPermissions": [{"name": "ohos.permission.INTERNET"}] 在页面对应js文件内&#xff0c;填写import fetch from system.fetch;。 GET和POST区别 GET将表单数…

界面控件DevExpress v24.1全新发布 - 跨平台性进一步增强

DevExpress拥有.NET开发需要的所有平台控件&#xff0c;包含600多个UI控件、报表平台、DevExpress Dashboard eXpressApp 框架、适用于 Visual Studio的CodeRush等一系列辅助工具。屡获大奖的软件开发平台DevExpress 今年第一个重要版本v23.1正式发布&#xff0c;该版本拥有众多…

1. 基础设计流程(以时钟分频器的设计为例)

1. 准备工作 1. 写有vcs编译命令的run_vcs.csh的shell脚本 2. 装有timescale&#xff0c;设计文件以及仿真文件的flish.f&#xff08;filelist文件&#xff0c;用于VCS直接读取&#xff09; vcs -R -full64 -fsdb -f flist.f -l test.log 2. 写代码&#xff08;重点了解代码…

【Mac】DMG Canvas for mac(DMG镜像制作工具)软件介绍

软件介绍 DMG Canvas 是一款专门用于创建 macOS 磁盘映像文件&#xff08;DMG&#xff09;的软件。它的主要功能是让用户可以轻松地设计、定制和生成 macOS 上的安装器和磁盘映像文件&#xff0c;以下是它的一些主要特点和功能。 主要特点和功能 1. 用户界面设计 DMG Canva…

定义和反射Annotation类(注解)

文章目录 前言一、定义Annotation类二、反射Anootation类 1.元注解2.反射注解总结 前言 在写代码的过程中&#xff0c;我们经常会写到注释&#xff0c;以此来提醒代码中的点。但是&#xff0c;这些注释不会被查看&#xff0c;也不在整个代码之中&#xff0c;只能在源代码中进行…

Mistral AI最新力作——Mistral Large媲美GPT-4

Mistral AI自豪地宣布&#xff0c;他们的最新力作——Mistral Large&#xff0c;已经正式面世。这款尖端的文本生成模型不仅在多语言理解上表现出色&#xff0c;更在推理能力上达到了顶级水平。Mistral Large能够处理包括文本理解、转换和代码生成在内的复杂多语言推理任务。 M…

依赖注入(Dependency Injection, DI)在 iOS 开发中的应用

在 iOS 开发中&#xff0c;我们经常会遇到类与类之间存在依赖关系的情况。例如&#xff0c;一个视图控制器可能需要一个服务对象来处理数据&#xff0c;这种情况下&#xff0c;视图控制器就依赖于这个服务对象。传统的做法是直接在视图控制器中创建服务对象&#xff0c;但这会导…

目标跟踪算法(bytetrack)-tensorrt部署教程

一、本机安装python环境 conda create -n bytetrace_env python=3.8 activate bytetrace_env conda install pytorch torchvision cudatoolkit=10.1 -c检测GPU是否可用,不可用不行 import torch print(torch.cuda.is_available())安装bytetrack git clone https://github.c…

车辆轨迹预测系列 (二):常见数据集介绍

车辆轨迹预测系列 (二)&#xff1a;常见数据集介绍 文章目录 车辆轨迹预测系列 (二)&#xff1a;常见数据集介绍1、NuScenes (2020)&#xff1a;1、下载2、说明 2、Waymo Open Dataset (2020)&#xff1a;1、介绍2、概述3、下载4、教程5、参考 3、Lyft Level 5 (2020)&#xff…

Ubuntu系统如何配置通过图形界面登录root用户

Ubuntu系统中的root账号默认是锁定的&#xff0c;但可以通过设置密码来启用。 需要注意的是&#xff0c;由于root用户具有对系统完全控制的权限&#xff0c;因此在使用root账户时应格外小心。一个错误的命令可能会导致系统损坏&#xff0c;这就是为什么Ubuntu默认不启用root账户…

ELK Kibana搜索框模糊搜索包含不包含

默认是KQL,点击切换Lucene搜索&#xff0c;搜索日志中包含Exception关键字&#xff0c;不包含BizException、IllegalArgumentException、DATA_SYNC_EXCEPTION关键字的日志&#xff0c;如下&#xff1a; message: *Exception AND !(message : *BizException OR message : *Ille…

五十三、openlayers官网示例Layer Spy解析——跟随鼠标透视望远镜效果、图层剪裁

官网demo地址&#xff1a; Layer Spy 这篇实现了鼠标跟随望远镜效果&#xff0c;鼠标移动时绘制一个圆形的剪裁区剪裁上层图层。 container.addEventListener("mousemove", function (event) {mousePosition map.getEventPixel(event);map.render();});container.a…

工具分享:Search_Viewer

文章目录 前言Search_Viewer介绍安装方式使用方式 前言 本文推荐工具Search_Viewer&#xff0c;详细介绍其安装和使用方式。 Search_Viewer介绍 集Fofa、Hunter鹰图、Shodan、360 quake、Zoomeye 钟馗之眼、censys 为一体的空间测绘gui图形界面化工具&#xff0c;支持一键采…

【Python】成功解决TypeError: missing 1 required positional argument

【Python】成功解决TypeError: missing 1 required positional argument 下滑即可查看博客内容 &#x1f308; 欢迎莅临我的个人主页 &#x1f448;这里是我静心耕耘深度学习领域、真诚分享知识与智慧的小天地&#xff01;&#x1f387; &#x1f393; 博主简介&#xff1…

事件驱动架构详解:触发与响应构建高效系统

目录 前言1. 事件驱动架构概述1.1 什么是事件1.2 事件驱动架构的核心概念 2. 事件驱动架构的实现2.1 基于消息队列的实现2.2 基于发布-订阅模式的实现2.3 基于流处理的实现 3. 事件驱动架构的优势3.1 松耦合性3.2 可扩展性3.3 异步处理3.4 灵活性 4. 事件驱动架构的应用场景4.1…

【论文复现|智能算法改进】改进麻雀算法的无人机三维路径规划

目录 1.UAV路径规划数学模型2.改进点3.结果展示4.参考文献5.代码获取 1.UAV路径规划数学模型 【智能算法应用】蜣螂优化算法DBO求解UAV路径规划 2.改进点 Logistics混沌映射 X n 1 μ X n ( 1 − X n ) , X n ∈ ( 0 , 1 ) (1) X_{_{n1}} \mu X_{_n}( 1 - X_{_n} ) ,\qua…

CSS属性选择器具有不区分大小写的模式

今天&#xff0c;我偶然发现了 caniuse.com 项目的一期&#xff0c;其中提到了新的和即将推出的 CSS Level 4 选择器。 这个列表很长&#xff0c;并且有许多新的选择器正在开发中。一个新的选择器标志引起了我的注意&#xff1b;属性选择器将变成一个 i 标志&#xff0c;这使得…

CRMEB PRO企业微信通讯录配置

企业微信通讯录配置 登录企业微信管理后台 企业微信 1、点击【管理工具】找到【通讯录同步】点击进入 2、点击【开启API接口同步】 进入设置【通讯录同步】页面后&#xff0c;权限一栏&#xff0c;勾选【API编辑通讯录】勾选【开启手动编辑】&#xff1b; 3、点击下图箭头所…