自己开发完整项目一、登录功能-04(集成jwt)

news2024/12/23 18:42:39

一、说明

前面文章我们写到了通过数据库查询出用户信息并返回,那么在真实的项目中呢,后端是需要给前端返回一个tocken,当前端通过登录功能认证成功之后,我们后端需要将用户信息和权限整合成一个tocken返回给前端,当前端再次访问别的接口的时候,需要携带着tocken,请求到达后端的时候,后端需要从前端传递过来的tocken中解析出用户信息和权限,判断是否可以访问。


实现思路:在登录中的查询用户中,将权限也查询出来,随后通过jwt来生成tocken和解析tocken

二、 编写jwt工具类(生成tocken和解析tocken)

package com.ljy.myspringbootlogin.utils;

import io.jsonwebtoken.*;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.security.Keys;
import org.springframework.stereotype.Component;

import javax.crypto.SecretKey;
import java.util.Date;
import java.util.Map;

@Component
public class JwtUtils {

    /**
     * 创建一个密钥
     */

    private String secret = "Ljy991008X123435asdfSFS34wfsdfsdfSDSD32dfsddDDerQSNCK34SOWEK5354fdgdf4";

    SecretKey  key = Keys.hmacShaKeyFor(Decoders.BASE64.decode(secret));


    /**
     * 生成tocken
     */
    public String createTocken(Map<String,Object> map){
        String tocken = Jwts.builder()
                .setClaims(map)
                .issuedAt(new Date())
                .expiration(new Date(System.currentTimeMillis() * 30 * 60 * 1000))
                .signWith(key)
                .compact();
        return tocken;
    }

    /**
     * 解析tocken
     */
    public Claims parasTocken(String tocken){
        Jws<Claims> claimsJws = Jwts.parser()
                .verifyWith(key)
                .build()
                .parseSignedClaims(tocken);

        return claimsJws.getBody();
    }
}

三、通过jwt工具类将用户信息和权限生成tocken返回给前端

修改userServiceImpl中的代码

package com.ljy.myspringbootlogin.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ljy.myspringbootlogin.commont.Reuslt;
import com.ljy.myspringbootlogin.mapper.UserMapper;
import com.ljy.myspringbootlogin.model.UserModel;
import com.ljy.myspringbootlogin.service.IUserService;
import com.ljy.myspringbootlogin.utils.JwtUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import java.util.*;

@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, UserModel> implements IUserService {

    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private JwtUtils jwtUtils;

    @Override
    public Reuslt<UserModel> login(String username, String password) {
        System.out.println("进入serviceimpl");
        System.out.println(username);
        System.out.println(password);
        //传入用户名和密码
        UsernamePasswordAuthenticationToken tocken = new UsernamePasswordAuthenticationToken(username, password);
        System.out.println("tocken"+tocken);
        //实现登录,此时就会调用loadUserByName
        //authenticate其实就是userdetails
        Authentication authenticate = null;
        try{
            authenticate= authenticationManager.authenticate(tocken);
            System.out.println("測試!!!!!");
        }catch (BadCredentialsException e){
            return Reuslt.error(500,"用户名或者密码错误");
        }
        UserModel principal = (UserModel)authenticate.getPrincipal();
        System.out.println("principal:"+principal);

        //生成tocken   修改这里就可以
        HashMap<String,Object> map = new HashMap<>();
        map.put("id",principal.getId());
        map.put("username",principal.getUsername());
        map.put("menuList",principal.getMenuList());
        map.put("roleList",principal.getRoleList());
        String tocken1 = jwtUtils.createTocken(map);
        System.out.println("tocken1"+tocken1);
        principal.setTocken(tocken1);
        return Reuslt.ok(principal);
    }
}

四、编写自定义过滤器(作用:当前端携带tocken访问别的接口的时候,需要进行判断是否有权限访问)

package com.ljy.myspringbootlogin.filter;

import com.ljy.myspringbootlogin.model.MenuModel;
import com.ljy.myspringbootlogin.model.RoleModel;
import com.ljy.myspringbootlogin.model.UserModel;
import com.ljy.myspringbootlogin.utils.JwtUtils;
import io.jsonwebtoken.Claims;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 这个过滤器的作用:
 * 当某一个用户登录认证成功之后,后端给前端返回一个tocken信息,这个tocken信息里面包含着用户的属性和用户的角色、权限信息
 * 那么当这个用户再次访问别的接口的时候,后端就需要对前端传递过来的tocken信息进行解析,判断里面是否有权限访问接口
 *
 * 我们需要集成OncePerRequestFilter,这个是一个抽象类,其中有一个doFilter方法,在doFilter方法中调用了doFilterInternal方法,也是一个抽象方法,所以需要实现
 * 并且只会在请求之前执行一次
 *
 * 实现逻辑:
 * 1.获取前端传递过来的tocken
 * 2.解析tocken
 * 3.将解析出来的用户信息和权限使用Authentication告诉给springSecurity框架,springsecurity会将信息存储到SecurityContet中,
 * 从而放在SecurityContetHolder中
 * 4.有权限就访问,没有权限就报错
 *
 *
 * 注意:登录的时候,只需要放用户名和密码
 *      登录成功之后请求别的接口的时候,需要放的是用户信息和用户权限
 *
 */

@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
    @Autowired
    JwtUtils jwtUtils;

    /**
     * 这个方法会被doFilter调用
     * @param request
     * @param response
     * @param filterChain
     * @throws ServletException
     * @throws IOException
     */
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {


        /**
         * 1.获取tocken
         * 因为前端将tocken信息放到了请求头中,所以我们需要使用request.getHeader("XXXX")来获取tocken
         */
        String tocken = request.getHeader("Authorization");
        if(tocken == null){
            doFilter(request,response,filterChain);
            return;
        }

        /**
         * 2.解析tocken
         * 使用我们自己编写的工具类,JwtUtils来解析
         */
        Claims claims = jwtUtils.parasTocken(tocken);
        System.out.println("解析出来的用户信息:"+claims);

        /**
         * 3.将解析出来的用户信息和权限使用Authentication告诉给springSecurity框架,springsecurity会将信息存储到SecurityContet中,
         * 从而放在SecurityContetHolder中
         */
        //3.1 从tocken中拿到信息
        Long id = claims.get("id", Long.class);
        String username = claims.get("username", String.class);
        List<String> menuList = claims.get("menuList", ArrayList.class);
        System.out.println("menuList:"+menuList);
        List<String> roleList = claims.get("roleList", ArrayList.class);
        //3.2 将信息放到userModel中
        UserModel userModel = new UserModel();
        userModel.setId(id);
        userModel.setUsername(username);
        userModel.setMenuList(menuList);
        //将权限信息转换成
        Collection<GrantedAuthority> grantedAuthorities = AuthorityUtils.createAuthorityList(menuList.toArray(new String[0]));
        userModel.setAuthorities(grantedAuthorities);
        System.out.println("userModel:"+userModel);
        //3.3 将信息当道SecurityContet中
        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
                new UsernamePasswordAuthenticationToken(userModel, "", userModel.getAuthorities());
        SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
        System.out.println("userModel.getAuthorities():"+userModel.getAuthorities());
        //3.4 放行
        doFilter(request,response,filterChain);

    }
}

五、将自定义过滤器加载到springsecurity框架的过滤器链中
修改springsecurityConfig

package com.ljy.myspringbootlogin.config;

import com.ljy.myspringbootlogin.filter.JwtAuthenticationFilter;
import com.ljy.myspringbootlogin.springSecurityService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@EnableWebSecurity //开启springSecurity,会注册大量的过滤器链
@Configuration
@EnableMethodSecurity //开启方法级别的安全校验
public class springSecurityConfig {

    @Autowired
    private springSecurityService springSecurityService;
    @Autowired
    private JwtAuthenticationFilter jwtAuthenticationFilter;

    /**
     * 配置过滤器链
     * @param http
     * @return
     * @throws Exception
     */

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception{
        http.csrf().disable();//跨域漏洞防御:关闭
        http.cors().disable();//跨域拦截关闭
        http.authorizeHttpRequests()
                .antMatchers("/user/**").permitAll()
                .anyRequest().authenticated();

        //将自己定义的过滤器添加到过滤器链中
        //将自定义过滤器放到认证过滤器之前
        http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
        return http.build();
    }


    /**
     * AuthenticationManager:负责认证,也就是认证规则
     * DaoAuthenticationProvider:负责将springSecurityService和passwordEncoder放进AuthenticationManager中
     * @return
     */

    @Bean
    public AuthenticationManager authenticationManager(){

        System.out.println("進入authenticationManager");
        DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
        daoAuthenticationProvider.setUserDetailsService(springSecurityService);
        //关联使用的密码编码器
        daoAuthenticationProvider.setPasswordEncoder(passwordEncoder());
        //将daoAuthenticationProvider放到ProviderManager中
        ProviderManager providerManager = new ProviderManager(daoAuthenticationProvider);
        System.out.println("結束authenticationManager");
        return providerManager;

    }

    /**
     * 密码编码器
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder(){
        return NoOpPasswordEncoder.getInstance();
    }
}

六、编写一个测试接口进行验证

package com.ljy.myspringbootlogin.controller;

import com.ljy.myspringbootlogin.commont.Reuslt;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {

    //创建登录控制器
    @RequestMapping("/test01")
    @PreAuthorize("hasAnyAuthority('qxgl')")  //需要的权限
    public Reuslt<String> aaa(){
        String a="测试";
        return Reuslt.ok(a);
    }

}

七、postman测试

1.登录生成tocken

2.访问测试接口,并写到上面的tocken

 

八、引入jwt依赖

<!--        引入jwt-->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-api</artifactId>
            <version>0.12.6</version>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-impl</artifactId>
            <version>0.12.6</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-jackson</artifactId> 
            <version>0.12.6</version>
            <scope>runtime</scope>
        </dependency>

 九、总结

到目前位置,我们实现了前端登录成功之后,通过数据库查询用户信息,并将用户信息生成tocken返回给前端,当前端携带tocken访问权限控制接口的时候,会判断是否有权限,进而判断是否可以访问。
但是存在一个问题,在上面的代码中,我们的接口权限是通过注解的方式写死的,在真实项目中,这个是绝对不可以的,所以我们需要动态实现权限,我们在下一章进行编写!

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

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

相关文章

【Python技术】使用langchain、fastapi、gradio生成一个简单的智谱AI问答界面

前几天&#xff0c;智谱AI BigModel开放平台宣布&#xff1a;GLM-4-Flash 大模型API完全免费了&#xff0c;同时开启了GLM-4-Flash 限时免费微调活动。对想薅免费大模型羊毛的个人玩家&#xff0c;这绝对是个好消息&#xff0c;我们不仅可以免费使用BigModel开放平台上的GLM-4-…

产品入门篇笔记

产品和产品经理 产品&#xff1a;解决某个问题的物品&#xff0c;无形、有形都可以。 产品经理&#xff1a;简单而言就是想清楚怎么做的人&#xff0c;需要想清楚产品怎么设计&#xff0c;要分析什么用户、在什么场景、怎么样的需求&#xff1b;然后考虑产品的功能、优势、价值…

LeetCode_sql_day20(1398.购买了产品A和产品B却没有购买产品C的顾客)

描述: Customers 表&#xff1a; ------------------------------ | Column Name | Type | ------------------------------ | customer_id | int | | customer_name | varchar | ------------------------------ customer_id 是这张表中具有唯一…

李沐讲座:大语言模型的实践经验和未来预测 | 上海交大

本文简介 本博客记录了李沐关于语言模型与职业生涯分享的精彩讲座&#xff0c;涵盖了大语言模型的核心要素、工程实践中的挑战&#xff0c;以及演讲者个人职业生涯中的心得体会。 李沐简介 李沐&#xff08;Mu Li&#xff09;是一位在人工智能与深度学习领域具有广泛影响力的…

电感的分类

电感作为电子电路中的重要元件&#xff0c;具有多种分类方式&#xff0c;每种类型的电感都有其独特的优缺点。以下是对电感分类及其优缺点的详细分析&#xff1a; 一、按工作频率分类 高频电感&#xff1a;适用于高频电路&#xff0c;具有较高的自谐振频率和较低的损耗。 优点…

【学习笔记】 陈强-机器学习-Python-Ch13 提升法

系列文章目录 监督学习&#xff1a;参数方法 【学习笔记】 陈强-机器学习-Python-Ch4 线性回归 【学习笔记】 陈强-机器学习-Python-Ch5 逻辑回归 【课后题练习】 陈强-机器学习-Python-Ch5 逻辑回归&#xff08;SAheart.csv&#xff09; 【学习笔记】 陈强-机器学习-Python-…

【python计算机视觉编程——图像聚类】

python计算机视觉编程——图像聚类 6.图像聚类6.1 K-means聚类6.1.2 图像聚类6.1.3 在主成分上可视化图像6.1.4 像素聚类 6.2 层次聚类6.3 谱聚类 6.图像聚类 6.1 K-means聚类 from scipy.cluster.vq import * import numpy as np from pylab import * matplotlib.rcParams[f…

第二篇——勾股定理:为什么在西方教毕达哥拉斯定理?

目录 一、背景介绍二、思路&方案三、过程1.思维导图2.文章中经典的句子理解3.学习之后对于投资市场的理解4.通过这篇文章结合我知道的东西我能想到什么&#xff1f; 四、总结五、升华 一、背景介绍 数学大厦的建立以及与自然科学的逻辑关系&#xff0c;以及他们的边界在这…

sql-libs第三关详细解答

首先看看and 12会不会正常显示 结果正常显示&#xff0c;说明存在引号闭合 加了一个引号&#xff0c;发现报错信息中还存在括号&#xff0c;说明sql语句中有括号&#xff0c;那我们还要闭合括号 现在就好了&#xff0c;and 11正常&#xff0c;and 12不正常&#xff0c;那就开始…

常用高性能架构模式

《从0开始学架构》里讲述了常用的高性能架构模式&#xff0c;这里面很多大家可能也都用过了&#xff0c;我应该也写过相关的技术文章。正好按照书里的思路重新梳理一次。 一、读写分离 读写分离的基本原理是将数据库读写操作分散到不同的节点上 感想&#xff1a; 读写分离应…

Go入门:gin框架极速搭建图书管理系统

Go入门:gin框架极速搭建图书管理系统 前言 本项目适合 Golang 初学者,通过简单的项目实践来加深对 Golang 的基本语法和 Web 开发的理解。 项目源码请私信,欢迎前往博主博客torna.top免费查看。 项目结构 D:. ├─ go.mod ├─ go.sum │ ├─ cmd │ └─ main │ …

Jenkins配置使用LDAP的用户和密码登录

# 检查配置文件是否正确 [rootlocalhost schema]# slaptest -u 62c6aafe ldif_read_file: checksum error on "/etc/openldap/slapd.d/cnconfig/olcDatabase{1}monitor.ldif" 62c6aafe ldif_read_file: checksum error on "/etc/openldap/slapd.d/cnconfig/olcD…

Java注解和JDK新特性

1. 注解 1.1. 认识注解 Annotation&#xff1a;JDK1.5新提供的技术 编译检查&#xff1a;比如SuppressWarnings, Deprecated和Override都具有编译检查的作用替代配置文件&#xff1a;使用反射来读取注解的信息 注解就是代码里的特殊标记&#xff0c;用于替代配置文件&#…

四大集合之Set

一、Set基础知识 1. Set集合 1.1 HashSet Set集合区别于其他三大集合的重要特性就是元素具有唯一性&#xff0c;南友们记不住这个特性的话&#xff0c;有个易记的方法。Set集合为什么要叫Set呢&#xff1f;因为Set集合的命名取自于我们小学数学里的集合论&#xff08;Set Th…

SPI(硬件协议)

1 SPI硬件外设协议 2 SPI框图 3 硬件SPI数据收发流程 1 发送数据&#xff0c;同时接收数据&#xff0c;相互配合&#xff0c;可以实现数据流不间断 2 全双工SPI&#xff0c;发送和接收数据寄存器分开&#xff0c;可以同时进行 4 spi传输框图 1 速度快 2 速度慢&#xff0c;容…

软考中项拿证利器:系统集成项目管理工程师(第3版)一站通关

指尖疯编著的《系统集成项目管理工程师&#xff08;适用第3版大纲&#xff09;一站通关》目前现货已经上线各大电商平台&#xff0c;您可以在任一电商搜索《系统集成项目管理工程师&#xff08;适用第3版大纲&#xff09;一站通关》即刻找到。 出版中项一站通关完全是机缘巧合&…

Nginx: 性能优化之提升CPU效率以及TCP的三次握手和四次挥手

提升利用CPU的效率 1 &#xff09;CPU的调度机制 现在来看下 linux中 CPU的一个调度机制 假设现在系统上有只有一颗CPU&#xff0c;而linux系统是一个多任务的一个操作系统 它允许我们各个不同的用户允许在同一个操作系统上执行很多个进程 单核CPU肯定不可能同时去执行这样一…

5.图论.题目2

5.图论.题目2 题目8.字符串接龙9.有向图的完全可达性10.岛屿的周长11.寻找存在的路径12.冗余连接113.冗余连接214.寻宝 题目 8.字符串接龙 题目链接 本题的直观思路如下图所示&#xff1b;但该题有两个问题&#xff1a;1.图中的线是如何连接起来的 2.如何确定起点到终点的最…

《JavaEE进阶》----4.<SpringMVC①简介、基本操作(各种postman请求)>

本篇博客讲解 MVC思想、及Spring MVC&#xff08;是对MVC思想的一种实现&#xff09;。 Spring MVC的基本操作、学习了六个注解 RestController注解 RequestMappering注解 RequestParam注解 RequestBody注解 PathVariable注解 RequestPart注解 MVC View(视图) 指在应⽤程序中…

数据同步的艺术:探索PostgreSQL和Redis的一致性策略

作者&#xff1a;后端小肥肠 &#x1f347; 我写过的文章中的相关代码放到了gitee&#xff0c;地址&#xff1a;xfc-fdw-cloud: 公共解决方案 &#x1f34a; 有疑问可私信或评论区联系我。 &#x1f951; 创作不易未经允许严禁转载。 1. 前言 在当今高度数字化的世界中,应用程…