SpringbootSecurity登陆验证(前后端分离)

news2024/11/16 9:44:34

一、什么是jwt

JWT全称是JSON Web Token,如果从字面上理解感觉是基于JSON格式用于网络传输的令牌。实际上,JWT是一种紧凑的Claims声明格式,旨在用于空间受限的环境进行传输,常见的场景如HTTP授权请求头参数和URI查询参数。JWT会把Claims转换成JSON格式,而这个JSON内容将会应用为JWS结构的有效载荷或者应用为JWE结构的(加密处理后的)原始字符串,通过消息认证码(Message Authentication Code或者简称MAC)和/或者加密操作对Claims进行数字签名或者完整性保护。

二、创建后端项目

2.1 添加pom依赖

        <!-- hutool 依赖 -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.16</version>
        </dependency>

2.2 编辑entity、mapper、service、controller

这里就不过多写了,依旧是跟上一边的文章一样

2.3 JWT 过滤器类

package com.aaa.filter;

import cn.hutool.jwt.JWT;
import cn.hutool.jwt.JWTUtil;
import com.aaa.until.ResponseMsg;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import java.util.stream.Collectors;

@Component
public class JWTFilter extends OncePerRequestFilter {


    /**
     * 重写的doFilterInternal方法
     * 解析token并验证用户信息
     * 如果验证成功,则保存用户信息并放行
     * 如果验证失败,则返回错误信息
     * 如果token为空且请求路径不在白名单中,则返回错误信息
     */
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        // 白名单路径
        String[] whitename = {"/login"};
        // 获取请求头中的token
        String token = request.getHeader("token");
        // 如果token不为空
        if (StringUtils.isNotBlank(token)) {
            // 验证token是否有效
            boolean verify = JWTUtil.verify(token, "user".getBytes());
            // 如果验证通过
            if (verify) {
                // 解析token,获取用户名和资源信息
                JWT jwt = JWTUtil.parseToken(token);
                String username = (String) jwt.getPayload("username");
                List<String> resources = (List<String>) jwt.getPayload("resources");

                // 将资源信息转换为SimpleGrantedAuthority列表
                List<SimpleGrantedAuthority> resourceList = resources.stream().map(res -> new SimpleGrantedAuthority(res)).collect(Collectors.toList());

                // 保存用户信息
                UsernamePasswordAuthenticationToken userPwdToken = new UsernamePasswordAuthenticationToken(username, null, resourceList);
                SecurityContextHolder.getContext().setAuthentication(userPwdToken);

                // 放行
                filterChain.doFilter(request, response);
            } else {
                // 验证失败,返回错误信息
                ResponseMsg responseMsg = new ResponseMsg(401, "没有登陆", null);
                printJsonData(response, responseMsg);
            }
        } else {
            // 如果token为空,检查请求路径是否在白名单中
            String requestURI = request.getRequestURI();

            // 如果在白名单中,放行
            if (ArrayUtils.contains(whitename, requestURI)) {
                filterChain.doFilter(request, response);
            } else {
                // 如果不在白名单中,返回错误信息
                ResponseMsg responseMsg = new ResponseMsg(401, "没有登陆", null);
                printJsonData(response, responseMsg);
            }
        }
    }

    public void printJsonData(HttpServletResponse response, ResponseMsg responseMsg) {
        try {
            response.setContentType("application/json;charset=utf8");  // 设置响应内容类型为JSON,并指定编码为UTF-8
            ObjectMapper objectMapper = new ObjectMapper();
            String json = objectMapper.writeValueAsString(responseMsg);  // 使用ObjectMapper将ResponseMsg对象转化为JSON字符串
            PrintWriter writer = response.getWriter();
            writer.print(json);  // 将JSON字符串写入响应输出流
            writer.flush();//刷新
            writer.close();//关闭
        } catch (Exception e) {
            e.printStackTrace();  // 打印异常堆栈信息
        }
    }
}

2.4 跨域配置CrossConfig

package com.aaa.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

@Configuration
public class CrossConfig {
    @Bean
    public CorsFilter corsFilter() {
        final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        final CorsConfiguration corsConfiguration = new CorsConfiguration();
        //corsConfiguration.setAllowCredentials(true);允许携带cookie
        corsConfiguration.addAllowedHeader("*"); // 允许所有的头
        corsConfiguration.addAllowedOrigin("*"); // 允许所有的请求源
        corsConfiguration.addAllowedMethod("*");  // 所欲的方法   get post delete put
        source.registerCorsConfiguration("/**", corsConfiguration); // 所有的路径都允许跨域
        return new CorsFilter(source);
    }

}

2.5 SecurityConfig 配置类

package com.aaa.config;

import cn.hutool.jwt.JWTUtil;
import com.aaa.filter.JWTFilter;
import com.aaa.until.ResponseMsg;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Resource
    protected UserDetailsService userDetailsService;
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }
    @Resource
    public JWTFilter jwtFilter;


    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //.usernameParameter("username")//登陆账号
        //.passwordParameter("userpassword");//登陆密码
        //.defaultSuccessUrl("/test");//登陆成功的跳转路径


        // 配置登录页面和登录处理路径
        http.formLogin().loginPage("/login.html")//路径前面必须加  /
                .loginProcessingUrl("/login")//跟提交的路径一样
                // 登录成功的处理逻辑
                .successHandler((request, response, authentication) ->{
                    Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();//获取资源信息
                    List<String> resources = authorities.stream().map(auth -> auth.getAuthority()).collect(Collectors.toList());//转为stream流变成list
                    Map map = new HashMap<>();
                    map.put("username",authentication.getName());
                    map.put("resources",resources);
                    //生成token
                    String token = JWTUtil.createToken(map, "user".getBytes());
                    ResponseMsg responseMsg = new ResponseMsg(200,"登陆成功",token);
                    //响应返回信息
                    printJsonData(response,responseMsg);
                })
                // 登录失败的处理逻辑
                .failureHandler((request, response, exception)->{
                    ResponseMsg responseMsg = new ResponseMsg(400,"验证失败");
                    printJsonData(response,responseMsg);
                });

        // 配置权限不允许的处理逻辑
        http.exceptionHandling().accessDeniedHandler((request, response, accessDeniedException) -> {
            ResponseMsg responseMsg = new ResponseMsg(403,"权限不允许",null);
            printJsonData(response,responseMsg);
        });

        // 配置不需要验证的路径
        http.authorizeRequests().antMatchers("/login.html", "login").permitAll();//不用验证
        //http.authorizeRequests().antMatchers("/test").hasRole("test");//必须有哪个权限才能访问
        //http.authorizeRequests().antMatchers("/test").hasAnyAuthority("resource");//必须有哪个资源才能访问

        // 配置其他路径需要验证
        http.authorizeRequests().anyRequest().authenticated();

        // 添加JWT过滤器
        http.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);

        //关闭csrf保护
        http.csrf().disable();

        // 配置跨域
        http.cors();

    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }


    public void printJsonData(HttpServletResponse response, ResponseMsg responseMsg) {

        try {
            response.setContentType("application/json;charset=utf8");  // json格式   编码是中文
            ObjectMapper objectMapper = new ObjectMapper();
            String s = objectMapper.writeValueAsString(responseMsg);// 使用ObjectMapper将result转化json为字符串
            PrintWriter writer = response.getWriter();
            writer.print(s);
            writer.flush();
            writer.close();
        }catch (Exception e){
            e.printStackTrace();

        }
    }
}

2.6 SecurityService类

也是跟上一篇的笔记一样

三、创建前端vue项目

login页面

不要忘了在路由下添加login页面

<template>
  <div>
    <el-form style="width: 400px ;margin: auto auto" :model="ruleForm" status-icon :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm">
      <el-form-item label="名字" prop="username">
        <el-input type="text" v-model="ruleForm.username" autocomplete="off"></el-input>
      </el-form-item>
      <el-form-item label="密码" prop="password">
        <el-input type="password" v-model="ruleForm.password" autocomplete="off"></el-input>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="submitForm('ruleForm')">提交</el-button>
        <el-button @click="resetForm('ruleForm')">重置</el-button>
      </el-form-item>
    </el-form>
  </div>
</template>

<script>
import qs from 'qs';
export default {
  data() {
    return {
      ruleForm: {
        username: 'user000',//每次刷新页面都要重新输入账号密码,所以直接把账号密码写入进来了
        password: '123456',
      },
      rules: {
        username: [
          {required: true, message: '请输入账户', trigger: 'blur'}
        ],
        password: [
          {required: true, message: '请输入密码', trigger: 'blur'}
        ]
      }
    };
  },
  methods: {
    submitForm(formName) {
      this.$refs[formName].validate((valid) => {
        if (valid) {
          alert('验证成功,准备提交')
          this.$axios.post('/login',qs.stringify(this.ruleForm)).then(res=>{
            console.log(res)
            if (res.data.code == 200){
              console.log(res.data.data);
//--------------------------------------------------------------
              sessionStorage.setItem("token",res.data.data)在sessionstorage中添加一个为token的数据
//--------------------------------------------------------------
              location.href='/main';//成功访问main页面
            }else {
              
            }
          })
          //location.href='/main'
        } else {
          alert('验证失败');
          return false;
        }
      });
    },
    resetForm(formName) {
      this.$refs[formName].resetFields();
    }
  }
}
</script>

在main.js添加下面的代码

验证token是否存在,如果有的话就在请求头里面添加token

// 请求拦截器
instance.interceptors.request.use(config => {
    if (sessionStorage.getItem("token")){
        let  token = sessionStorage.getItem("token");
        config.headers['token'] = token;
    }
    return config;
}, error => {
    return Promise.reject(error);
});

四、总结

从前端页面输入账号向后端发送跨域请求,后端接受参数验证是否在数据库中存在,如果存在就去把用户在数据库中的角色权限和资源权限查出来,保存在Security中.登陆成功后就把用户的信息用JWTUtil.createToken()方法生成token,返回给前端,前端拿到token,前端再次向后端发起请求时携带token,后端识别,如果正确的话就放行,不正确就访问失败

 
 

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

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

相关文章

基于若依的ruoyi-nbcio流程管理系统增加流程设计器支持自定义表单的选择与处理

更多ruoyi-nbcio功能请看演示系统 gitee源代码地址 前后端代码&#xff1a; https://gitee.com/nbacheng/ruoyi-nbcio 演示地址&#xff1a;RuoYi-Nbcio后台管理系统 因为之前不支持在流程设计器进行自定义业务表单的关联选择&#xff0c;所以这部分实现这个。 1、前端 对…

使用jedis连接虚拟机redis报错 Failed to connect to any host resolved for DNS name

问题描述&#xff1a; 导致该问题发生的原因可能是虚拟机没有开放6379端口。 解决方案&#xff1a; 首先检查redis.conf的bing配置是否被注释了&#xff0c;如果没有将其注释 第二步&#xff0c;将保护模式设置为no 第三步&#xff0c;接下来可以使用命令查看6379端口是否…

聚观早报 |京东11.11公布成绩单;2023数字科技生态大会

【聚观365】11月13日消息 京东11.11公布成绩单 2023数字科技生态大会 TikTok深受英国中小企业青睐 周鸿祎称大模型2年内可“进”智能汽车 双11全国快递业务量达 6.39 亿件 京东11.11公布成绩单 京东11.11公布成绩单&#xff1a;截至11月11日晚23:59&#xff0c;2023年京东…

ASD光谱仪使用

ASD光谱仪使用 光谱仪机器和电脑用来实时查看光谱曲线&#xff0c;以及控制光谱仪采集的时间、条数等各项参数。 在采集时&#xff0c;需要面向太阳&#xff0c;将待测的对象完全暴露于阳光下&#xff08;下图站位是错误的挡住光线了&#xff09;。探头放置于对象正上方50cm处…

Linux进程之通过系统调用创建进程[fork()函数]

文章目录 0.PID是什么?1.通过代码创建子进程--fork1.1fork()初识1.2通过系统调用创建进程1.3perror()函数的了解 2.fork()的进一步了解2.1通过代码了解2.2查看进程的指令 0.PID是什么? 进程PID&#xff08;Process ID&#xff09;是操作系统为每个正在运行的进程分配的唯一标…

jquery的$

jQuery是什么 jQuery是一个快速、简洁的JavaScript框架&#xff0c;jQuery设计的宗旨是“write Less&#xff0c;Do More”。 jQuery的$ 使用过jQuery的应该都知道jQuery的$&#xff0c;看到源码中的这一段就能知道相当于jquery的简写&#xff0c; jquery有两种用法&#x…

Python 日志记录器logging 百科全书 之 日志回滚

Python 日志记录器logging 百科全书 之 日志回滚 前言 在之前的文章中&#xff0c;我们学习了关于Python日志记录的基础配置。 本文将深入探讨Python中的日志回滚机制&#xff0c;这是一种高效管理日志文件的方法&#xff0c;特别适用于长时间运行或高流量的应用。 知识点&…

【OS】操作系统课程笔记 第七章 内存管理

目录 7.1 内存管理的功能 7.1.1 内存分配 7.1.2 地址转换 1. 空间的概念 2. 地址转换 7.1.3 存储保护 7.1.4 存储共享 7.1.5 存储扩充 7.2 程序的链接和加载 7.2.1 程序的链接 链接的分类 7.2.2 程序的加载 1. 加载器的功能 2. 装入方式分类 7.3 连续分配方式 7.…

CentOS 7镜像下载;VMware安装CentOS 7;解决新安装的虚拟机没有网络,无法ping通网络的问题

CentOS 7镜像下载&#xff1b;VMware安装CentOS 8.5&#xff1b;解决新安装的虚拟机没有网络&#xff0c;无法ping通网络的问题 CentOS 8.5镜像下载VMware安装CentOS 7解决新安装的虚拟机没有网络&#xff0c;无法ping通网络的问题写入配置文件 CentOS 8.5镜像下载 阿里提供的…

排序 算法(第4版)

本博客参考算法&#xff08;第4版&#xff09;&#xff1a;算法&#xff08;第4版&#xff09; - LeetBook - 力扣&#xff08;LeetCode&#xff09;全球极客挚爱的技术成长平台 本文用Java实现相关算法。 我们关注的主要对象是重新排列数组元素的算法&#xff0c;其中每个元素…

NSSCTF题库——web

[SWPUCTF 2021 新生赛]gift_F12 f12后ctrlf找到flag [SWPUCTF 2021 新生赛]jicao——json_decode() 加密后的格式 $json {"a":"php","b":"mysql","c":3}; json必须双引号传输 构造&#xff1a;GET里json{"x"…

【JUC】三、集合的线程安全

文章目录 1、ArrayList集合线程安全问题分析2、解决方式一&#xff1a;Vector或synchronizedList( )3、解决方式二&#xff1a;CopyOnWriteArrayList 写时复制4、HashSet集合线程不安全的分析与解决5、HashMap集合线程不安全的分析与解决 1、ArrayList集合线程安全问题分析 对…

TLP超线程技术

在实现IPL指令级并行的同时实现TLP(Thread Level Parallelism)线程级并行实现多线程有两种主要的方法超线程即同时多线程&#xff0c;在单个处理器或单个核中设置了两套线程状态部件&#xff0c;共享高速缓存和功能部件当两个线程同时需要某个资源时&#xff0c;其中一个线程必…

VuePress介绍及使用指南

VuePress是一个基于Vue.js的静态网站生成工具&#xff0c;它专注于以Markdown为中心的项目文档。VuePress具有简单易用的特性&#xff0c;同时提供了强大的自定义和扩展性。在本文中&#xff0c;我们将介绍VuePress的基本概念&#xff0c;并提供一个简单的使用指南。 什么是Vue…

【C语言】

C语言 1. C语言基础1.1 数据类型和占位符1.2 异或1.3 关键字1.4 const1.5 extern1.6 typedef1.7 static1.8 左值和右值1.9 位进行操作赋值 2. C指针3. 二维数组和指针4. 函数传递二维数组4.1 形参给出第二维的长度。4.2 形参声明为指向数组的指针。4.3 形参声明为指针的指针。 …

快速掌握队列的基础知识

目录 队列的特点基于链表实现队列用栈实现队列用队列实现栈 队列是一种线性数据结构&#xff0c;它只允许在一边进行插入操作&#xff08;队尾&#xff09;&#xff0c;另一边进行删除操作&#xff08;队头&#xff09;。插入操作称为入队&#xff0c;删除操作称为出队。队列遵…

【网络奇缘】我和英特网再续前缘

&#x1f308;个人主页: Aileen_0v0&#x1f525;系列专栏: 一见倾心,再见倾城 --- 计算机网络~&#x1f4ab;个人格言:"没有罗马,那就自己创造罗马~" 目录 计算机网络的概念 计算机网络的功能 ⭐1.数据通信 ⭐2.资源共享 ⭐3.分布式处理 ⭐4.提高可靠性 ⭐…

Java17新增特性

前言 前面的文章&#xff0c;我们对Java9、Java10、Java11、Java12 、Java13、Java14、Java15、Java16 的特性进行了介绍&#xff0c;对应的文章如下 Java9新增特性 Java10新增特性 Java11新增特性 Java12新增特性 Java13新增特性 Java14新增特性 Java15新增特性 Java16新增特…

Eclipse打包Springboot项目

首先&#xff0c;在pom.xml文件中添加配置&#xff0c;修改mainClass主函数&#xff1a; <build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configurat…

【电路笔记】-戴维南定理(Thevenin‘s Theorem)

戴维南定理&#xff08;Thevenin’s Theorem&#xff09; 文章目录 戴维南定理&#xff08;Thevenins Theorem&#xff09;1、概述与定义2、戴维南模型确定3、一些线性电路的戴维南模型3.1 单电压源3.2 单电流源3.3 多电流/电压源 4、结论 在本文中&#xff0c;我们将介绍一种强…