【springboot】整合spring security 和 JWT

news2024/11/15 0:06:28

目录

  • 1. 整合spring security
    •       1. 导入依赖
    •       2. 配置类
    •       3. 实体类实现UserDetails接口
    •       4. 业务逻辑实现类实现UserDetailsService接口
    •       5. 控制类实现登录功能
    •       6. 测试登录功能
  • 2. 分析源码
    •       1. UsernamePasswordAuthenticationToken
    •       2. Authentication接口
    •       3. SecurityContextHolder类

1. 整合spring security

      1. 导入依赖

<!--        security依赖-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
        <scope>compile</scope>
    </dependency>
        <!--        该模块包含 security 命名空间解析代码和Java配置代码,如需要将XML命名空间进行配置或 Spring Security 的 Java 配置支持 -->
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-config</artifactId>
    </dependency>
<!--        该模块包含过滤器和相关的web安全基础设施代码,如servlet API依赖的东西,认证服务和基于URL的访问控制-->
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-web</artifactId>
    </dependency>

在这里插入图片描述在这里插入图片描述在这里插入图片描述

      2. 配置类

@Configuration
public class SecurityConfig{
    @Bean //配置加密器
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();// 加密方式
    }
    /*
       resUserBizImpl必须实现UserDetailsService接口
       UserDetailsService接口是spring security的用户认证接口
       用于从数据库中读取用户信息
     */
    private ResUserBizImpl resUserBiz;
    @Autowired //这里使用set方法注入,可以避免循环依赖
    public void setResUserBiz(ResUserBizImpl resUserBiz) {
        this.resUserBiz = resUserBiz;
    }
    @Bean //认证服务 组装组件
    public AuthenticationProvider authenticatorProvider() {DaoAuthenticationProvider provider =
                new DaoAuthenticationProvider();
        //设置密码加密器
        provider.setPasswordEncoder(passwordEncoder());
        //设置用户信息获取服务 获取用户信息
        provider.setUserDetailsService(resUserBiz);
        return provider;
    }
    @Bean //认证管理器
    public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration)
            throws Exception {
        //获取认证管理器
        return configuration.getAuthenticationManager();
    }
    @Bean //配置安全过滤器链
    public SecurityFilterChain securityFilterChain(HttpSecurity http)
            throws Exception {
        //取消默认的登录页面
        http.formLogin(AbstractHttpConfigurer::disable)
                //取消默认的登出页面
                .logout(AbstractHttpConfigurer::disable)
                //将自己的认证服务加入
                .authenticationProvider(authenticatorProvider())
                //禁用csrf保护
                .csrf(AbstractHttpConfigurer::disable)
                //禁用session,因为使用token
                .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                //禁用http基本认证,因为传输数据用的post,且请求体为JSON
                .httpBasic(AbstractHttpConfigurer::disable)
                //开放接口,除开放的接口外,其他接口都需要认证
                .authorizeHttpRequests(request -> request
                        .requestMatchers(HttpMethod.POST, "/user/login","/user/register","/user/logout").permitAll()
                        .requestMatchers(HttpMethod.GET, "/captcha/getCaptcha").permitAll()
                        .anyRequest().authenticated());
        return http.build();
    }
}

      3. 实体类实现UserDetails接口

      UserDetails接口是spring security提供的,用于封装用户信息,包括用户名、密码、角色等。通过这个接口,可以将用户的详细信息封装到UserDetails对象中,并通过UserDetailsService接口的实现类来提供用户信息。

@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Resuser implements Serializable, UserDetails {
    @TableId(type = IdType.AUTO)
    private Integer userid;
    private String username;
    // UserDetails接口中定义的密码字段名必须为password
    @TableField(value = "pwd")
    private String password; 
    private String email;
    //表中没有该字段 用于封装角色
    @TableField(exist = false)
    private String role="user";
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        // 返回一个SimpleGrantedAuthority对象,表示用户的角色
        return List.of(new SimpleGrantedAuthority(role));
    }

    @Override
    public String getPassword() {
        return this.password;
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;//表示账号没有过期
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;//表示账号没有被锁定
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;//表示密码没有过期
    }

    @Override
    public boolean isEnabled() {
        return true;//表示账号可用
    }
}

      4. 业务逻辑实现类实现UserDetailsService接口

      这里实现了UserDetailsService接口中的loadUserByUsername方法,用于根据用户名查询用户信息。

@Service
@Slf4j
public class ResUserBizImpl implements ResUserBiz, UserDetailsService {
    @Autowired
    private ResUserMapper resUserMapper;
    // 注入加密器
    @Autowired
    private PasswordEncoder passwordEncoder;
    
    @Override //UserDetailsService接口的方法
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        LambdaQueryWrapper<Resuser> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Resuser::getUsername,username);
        try{
            Resuser resuser = resUserMapper.selectOne(queryWrapper);
            return resuser;
        }catch (Exception e){
            log.error("用户不存在");
            return null;
        }
    }
}

      5. 控制类实现登录功能

@RestController
@RequestMapping("/user")
public class ResUserController {
    @Autowired
    private ResUserBizImpl resUserBiz;
    @Autowired // 注入认证管理器
    private AuthenticationManager authenticationManager;
    @Autowired // 注入jwt工具类
    private JwtUtil jwtUtil;
    @RequestMapping("/login")// /user/login
    public ResponseResult login(
            @RequestBody // 将请求体中的json数据映射为ResUserVO对象
            @Valid // 开启校验,必须有resuserVO对象才能通过校验
            ResUserVO user,HttpSession session){
        //加入验证码 因为将验证码存在在session中,所以需要从session中取出来
        String captcha = (String) session.getAttribute("captcha");
        //判断验证码是否正确
        if(!captcha.equalsIgnoreCase(user.getCaptcha())){
            return ResponseResult.error("验证码错误");
        }
        //使用authenticate()方法接收UsernamePasswordAuthenticationToken对象,并返回一个包含用户详细信息的 Authentication 对象。
        Authentication authentication = authenticationManager.authenticate(
                //将用户名和密码[和证书]封装到UsernamePasswordAuthenticationToken对象中,用来包装认证信息
                new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPwd())
        );
        //getContext()获取当前线程的SecurityContext,并返回一个SecurityContext对象。
        //setAuthentication()设置当前线程的SecurityContext,并将Authentication对象作为参数传入。
        SecurityContextHolder.getContext().setAuthentication(authentication);
        //使用getPrincipal()方法获取认证信息,并转换为UserDetails对象
        UserDetails userDetails = (UserDetails) authentication.getPrincipal();
        Resuser resuser = (Resuser) userDetails;
        //生成JWT负载
        Map payload = new HashMap();
        payload.put("userid",String.valueOf(resuser.getUserid()));
        payload.put("username",userDetails.getUsername());
        payload.put("role",resuser.getRole());
        payload.put("email",resuser.getEmail());
        //生成JWT令牌
        String token = jwtUtil.getToken(payload);
        //返回token,后面使用token进行认证
        return ResponseResult.ok("登录成功").setData(token);
    }
}

      登录认证流程图:

image.png

      JWT工具类:

/**
 * 提供Jwt工具类
 * 提供Token生成和验证方法
 */
@Component //托管spring容器
public class JwtUtil {
    // 密钥
    private static final String key = "3f2e1d4c5b6a79808f7e6d5c4b3a29181716151413121110";

    /**
     * 生成JWT令牌
     * @param payload
     * @return
     */
    public String getToken(Map payload){
        //设置头部信息
        Map headers = new HashMap();
        //设置签名算法
        headers.put("alg", "HS256");
        //设置令牌类型
        headers.put("typ", "JWT");

        //生成令牌
        String jwt = Jwts.builder()
                //设置头信息
                .setHeaderParams(headers)
                //设置负载信息
                .setClaims(payload)
                //使用HS256算法和密钥对JWT进行签名
                .signWith(SignatureAlgorithm.HS256,key)
                //将之前设置的头部信息、负载信息和签名信息组合成一个完整的JWT,并以字符串形式返回
                .compact();
        System.out.println(jwt);
        return jwt;
    }

    /**
     * 解析JWT令牌
     * @param token
     * @return
     */
    public Claims parseToken(String token){
        try{
            System.out.println("开始解析令牌...");
            Claims claims = Jwts.parser()//创建一个解析器
                    .setSigningKey(key)//设置验证签名的密钥
                    .parseClaimsJws(token)//传入需要进行解析的token并解析
                    .getBody();//从Jws<Claims>对象中获取负载信息
            System.out.println(claims);
            return claims;
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }
}

      使用JWT可参考:JWT生成、解析token

      6. 测试登录功能

在这里插入图片描述


2. 分析源码

      1. UsernamePasswordAuthenticationToken

      将用户的认证信息(如用户名和密码)封装成一个对象,以便在认证过程中使用。

public class UsernamePasswordAuthenticationToken extends AbstractAuthenticationToken {
    private static final long serialVersionUID = 620L;
    private final Object principal;
    private Object credentials;
    /*
        常用的构造函数
        principal:认证的主体信息,通常为用户名或用户对象。
        credentials:认证的凭证信息,通常为密码。
     */
    public UsernamePasswordAuthenticationToken(Object principal, Object credentials) {
        super((Collection)null);
        this.principal = principal;
        this.credentials = credentials;
        this.setAuthenticated(false);
    }
    /*
        常用的构造函数
        authorities:用户的权限列表。
     */
    public UsernamePasswordAuthenticationToken(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities) {
        super(authorities);
        this.principal = principal;
        this.credentials = credentials;
        super.setAuthenticated(true);
    }
    /*
        ....
     */
}

      2. Authentication接口

       Authentication 对象包含了用户的身份信息(如用户名)、凭证(如密码)以及用户的权限列表。通过SecurityContextHolder.getContext().setAuthentication(authentication)语句设置认证信息。认证成功后,Authentication 对象会被存储在 SecurityContext 中,以便在应用程序的其他部分获取当前用户的认证信息。

public interface Authentication extends Principal, Serializable {
    // 获取权限
    Collection<? extends GrantedAuthority> getAuthorities();
    // 获取凭证
    Object getCredentials();
    // 获取详情
    Object getDetails();
    // 获取主体信息
    Object getPrincipal();
    // 是否已认证
    boolean isAuthenticated();
    // 设置是否已认证
    void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;
}

      3. SecurityContextHolder类

      SecurityContextHolder 是 Spring Security 中的一个核心类,用于存储和获取当前用户的安全上下文信息。它提供了一系列静态方法,用于管理 SecurityContext 对象,该对象包含了用户的认证信息和授权信息。

public class SecurityContextHolder {
    public static SecurityContext getContext() {
        return strategy.getContext();
    }
    /*
        ....
     */
}

      SecurityContext接口提供了获取和设置认证信息的方法。

public interface SecurityContext extends Serializable {
    Authentication getAuthentication();
    void setAuthentication(Authentication authentication);
}

链接:

      spring security中文文档:https://springdoc.cn/spring-security/index.html

      SecurityContext接口提供了获取和设置认证信息的方法。

public interface SecurityContext extends Serializable {
    Authentication getAuthentication();
    void setAuthentication(Authentication authentication);
}

链接:

spring security中文文档:https://springdoc.cn/spring-security/index.html

参考文章:https://blog.csdn.net/m0_71273766/article/details/132942056?spm=1001.2014.3001.5501

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

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

相关文章

【c/c++】类型转换:隐式类型转换、强制类型转换

目录 前言 一、了解类型转换 二、隐式类型转换 1.适用场景 2.转换规则 三、强制类型转换 适用场景 使用规则 注意事项 前言 类型转换是编程中一个常见的现象。在我们进行编码的时候不经意间就发生了&#xff0c;但却能让整个程序运行得更加流畅。 但是这种不经意&am…

C++:STL之vector

1.vector的使用 1.1vector的定义 使用vector需要包含头文件 #include<vector> vector的构造 &#xff08;constructor&#xff09;构造函数声明接口说明vector() (重点)无参构造vector(size_type n,const value_type& val value_type())用n个val初始化并构造vecto…

Java11环境安装(Windows)

目录 1 Java11安装2 配置2.1 JavaHome配置2.2 CLASSPATH配置PATH路径配置 3 验证 1 Java11安装 从官网下载Java11安装包&#xff1a;jdk-11_windows-x64_bin.exe,安装时选择安装到D:\Java目录。 安装完目录结构如下&#xff1a; 2 配置 2.1 JavaHome配置 如下图所示配置JAV…

Ubuntu构建只读文件系统

本文介绍Ubuntu构建只读文件系统。 嵌入式系统使用过程中&#xff0c;有时会涉及到非法关机&#xff08;比如直接关机&#xff0c;或意外断电&#xff09;&#xff0c;这可能造成文件系统损坏&#xff0c;为了提高系统的可靠性&#xff0c;通常将根文件系统设置为只读&#xf…

Linux下进程间的通信--信号量

前言&#xff1a; 资源竞争&#xff1a; 资源竞争&#xff08;Race Condition&#xff09;是多线程或多进程环境中的一种常见问题&#xff0c;它发生在多个进程或线程并发访问和修改同一资源&#xff08;如内存位置、文件、数据库记录等&#xff09;时&#xff0c;而最终结果…

mysql学习教程,从入门到精通,SQL AND OR 运算符(12)

1、SQL AND & OR 运算符 在本教程中&#xff0c;您将学习如何在子句中使用ASELECT column1_name, column2_name, columnN_nameFROM table_nameWHERE condition1 AND condition2;ND&#xff06;OR运算符&#xff0c;WHERE以根据多个条件过滤记录。 1.1、根据条件选择记录 …

从代码直观理解Self-Attention和Cross-Attention的本质区别

Transformer的模型架构实际上非常简单&#xff0c;Self-Attention 和 Cross-Attention 仅仅是在 k, v上有所不同&#xff08;这里不讨论 mask&#xff09;。 论文原文&#xff1a;Attention Is All You Need 我们可以使用同一个 Attention 类来实现 Self-Attention 和 Cross-At…

day11-多线程

一、线程安全问题 线程安全问题出现的原因&#xff1f;存在多个线程在同时执行同时访问一个共享资源存在修改该共享资源 线程安全:多个线程同时修改同一个资源 取钱案例 小明和小红是一对夫妻&#xff0c;他们有一个共同的账户&#xff0c;余额是10万元 如果小明和小红同时来取…

速看!6款可以写论文的ai写作网站,这才是真正的论文神器!(含教程)

在当今信息爆炸的时代&#xff0c;AI写作工具的出现极大地提高了写作效率和质量。特别是对于需要撰写论文的学生和研究人员来说&#xff0c;这些工具提供了极大的便利。本文将重点介绍一款备受推荐的AI写作平台——千笔-AIPassPaper&#xff0c;并结合相关教程帮助用户更好地使…

【北京迅为】《STM32MP157开发板使用手册》- 第二十四章 STM32CubeIDE的初步使用

iTOP-STM32MP157开发板采用ST推出的双核cortex-A7单核cortex-M4异构处理器&#xff0c;既可用Linux、又可以用于STM32单片机开发。开发板采用核心板底板结构&#xff0c;主频650M、1G内存、8G存储&#xff0c;核心板采用工业级板对板连接器&#xff0c;高可靠&#xff0c;牢固耐…

校园水电费管理微信小程序的设计与实现+ssm(lw+演示+源码+运行)

校园水电费管理小程序 摘 要 随着社会的发展&#xff0c;社会的方方面面都在利用信息化时代的优势。互联网的优势和普及使得各种系统的开发成为必需。 本文以实际运用为开发背景&#xff0c;运用软件工程原理和开发方法&#xff0c;它主要是采用java语言技术和mysql数据库来…

基于SSM的学生信息管理系统(选课管理系统)的设计与实现 (含源码+sql+视频导入教程)

&#x1f449;文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1 、功能描述 基于SSM的学生信息管理系统&#xff08;选课管理系统&#xff09;13拥有三种角色 管理员&#xff1a;学生管理、教师管理、专业管理、课程管理、审批管理、课程表管理、开课管理、教室管…

高德地图JS API加载行政区边界AMap.Polygon

&#x1f916; 作者简介&#xff1a;水煮白菜王 &#xff0c;一位资深前端劝退师 &#x1f47b; &#x1f440; 文章专栏&#xff1a; 高德AMap专栏 &#xff0c;记录一下平时在博客写作中&#xff0c;总结出的一些开发技巧✍。 感谢支持&#x1f495;&#x1f495;&#x1f49…

大模型LLM之SpringAI:Web+AI(二)

2.2.2、ChatModel API(聊天模型API) 聊天模型太多了,这里只写OpenAI和Ollama ChatModel和ChatClient区别:ChatClient针对的是所有模型,共用一个客户端。而ChatModel是针对各个模型实现的。 (1)OpenAI 自动配置 <dependency><groupId>org.springframework…

vue3 内置组件 <Suspense>

官方文档&#xff1a; <Suspense> 指南-Suspense 官方提示&#xff1a; <Suspense> 是一项实验性功能。它不一定会最终成为稳定功能&#xff0c;并且在稳定之前相关 API 也可能会发生变化。 <Suspense>是一个内置组件&#xff0c;用来在组件树中协调对异步依…

git删除本地分支报错:error: the branch ‘xxx‘ is not fully merged

git删除本地分支报错&#xff1a;error: the branch xxx is not fully merged error: the branch xxx is not fully merged 直接&#xff1a; git branch -D xxx 就可以。 如果删除远程分支&#xff1a; git push origin --delete origin/xxx git强制删除本地分支 git branc…

如何将Git本地代码推送到Gitee云端仓库

如何将Git本地代码推送到Gitee云端仓库 在使用Git进行版本控制时&#xff0c;将本地代码推送到远程仓库是一个基本且重要的操作。本文将详细介绍如何将你的Git本地代码推送到Gitee&#xff08;码云&#xff09;云端仓库。Gitee是一个国内非常流行的代码托管平台&#xff0c;类…

NX—UI界面生成的文件在VS上的设置

UI界面保存生成的三个文件 打开VS创建项目&#xff0c;删除自动生成的cpp文件&#xff0c;将生成的hpp和cpp文件拷贝到项目的目录下&#xff0c;并且在VS项目中添加现有项目。 修改VS的输出路径&#xff0c;项目右键选择属性&#xff0c;链接器中的常规&#xff0c;文件路径D:…

线性代数 第七讲 二次型_标准型_规范型_坐标变换_合同_正定二次型详细讲解_重难点题型总结

文章目录 1.二次型1.1 二次型、标准型、规范型、正负惯性指数、二次型的秩1.2 坐标变换1.3 合同1.4 正交变换化为标准型 2.二次型的主要定理3.正定二次型与正定矩阵4.重难点题型总结4.1 配方法将二次型化为标准型4.2 正交变换法将二次型化为标准型4.3 规范型确定取值范围问题4.…

《中国制药设备行业市场现状分析与发展前景预测研究报告》

报告导读&#xff1a;本报告从国际制药设备发展、国内制药设备政策环境及发展、研发动态、供需情况、重点生产企业、存在的问题及对策等多方面多角度阐述了制药设备市场的发展&#xff0c;并在此基础上对制药设备的发展前景做出了科学的预测&#xff0c;最后对制药设备投资潜力…