spring安全代码学习

news2024/11/26 3:08:32

源代码地址
登录的时候调用整个代码的过程为:

1.在注入的时候,先运行两个Bean注入PasswordEncoder和AuthenticationManagerBean,然后运行configure函数

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;

    @Bean
    public PasswordEncoder passwordEncoder(){
        //只需要把BCryptPasswordEncoder对象注入Spring容器中
        //SpringSecurity就会使用PasswordEncoder来进行校验
        return new BCryptPasswordEncoder();
    }

    //自定义AuthenticationManager
    @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();
        http.addFilterBefore(jwtAuthenticationTokenFilter,UsernamePasswordAuthenticationFilter.class);
    }

    //调用
    //暴露自定义的AuthenticationManager
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception{
        return super.authenticationManagerBean();
    }
}

2.点击postman发送之后

(1)先运行OncePerRequestFilter实现类JwtAuthenticationTokenFilter中的doFilterInternal函数中的doFilterInternal函数方法进行过滤

@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {

    //redis的内容直接从容器中获取即可
    @Autowired
    private RedisCache redisCache;

    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
        //1.获取token
        String token = httpServletRequest.getHeader("token");
        if(!StringUtils.hasText(token))
        {
            //放行
            filterChain.doFilter(httpServletRequest,httpServletResponse);
            //这里可以放行的原因在于后面还有FilterSecurityInterceptor等其他过滤器,
            //如果没有认证后面还会被拦截下来
            return;
            //这里如果没有return,响应回来之后还会调用后面的代码
        }

        String userId;
        //2.解析token(响应不为空)
        try {
            Claims claims = JwtUtil.parseJWT(token);
            userId = claims.getSubject();
            //这里parseJWT就是一个解析过程,不需要过多深究
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("token非法");
        }

        //3.从redis中获取用户信息
        String rediskey = "login:"+userId;
        LoginUser loginUser = redisCache.getCacheObject(rediskey);
        if(Objects.isNull(loginUser))
        {
            throw new RuntimeException("用户未登录");
        }
        //前面存入的是loginUser类型,因此这里不需要强转

        //4.存入SecurityContextHolder,这里setAuthentication需要传入
        //TODO 获取权限信息封装到Authentication之中
        //Authentication类,因此loginUser不能直接放入进去,需要转换
        //成Authentication类
        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(loginUser,null,null);
        //!!!这里必须调用带有三个参数的UsernamePasswordAuthenticationToken,因为带有三个参数的UsernamePasswordAuthenticationToken
        //是已经认证过的UsernamePasswordAuthenticationToken
        //第三个属于权限信息
        SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
        //放行
        filterChain.doFilter(httpServletRequest,httpServletResponse);
        //放行之后到下一个过滤器
    }
    /***
     * 定义jwt认证过滤器:
     * 获取token、解析token获取其中的userid
     * 从redis中获取用户信息、存入SecurityContextHolder
     * 之前继承filter,可以实现filter接口 implements Filter
     * 默认的implements Filter可能会存在一定的问题,导致一个请求过来会被调用多次
     *
     * 这个过滤器肯定要在FilterSecurityInterceptor前面,否则直接发现未认证之后
     * 就会抛出异常,因此选择放在UsernamePasswordAuthenticationFilter前面的
     * 位置,配置放在继承WebSecurityConfigurerAdapter的SecurityConfig之中
     */
}

如果是第一次登录的情况下,这里运行的时候因为Header中没有token,
第一次登录截图
因此会在这直接返回

if(!StringUtils.hasText(token))
{
   //放行
   filterChain.doFilter(httpServletRequest,httpServletResponse);
   //这里可以放行的原因在于后面还有FilterSecurityInterceptor等其他过滤器,
   //如果没有认证后面还会被拦截下来
   return;
   //这里如果没有return,响应回来之后还会调用后面的代码
}

如果是第二次登录的时候,则这里会将输入的token转化成为之前的userId,
第二次登录
然后从redis之中去找userId对应的类是否为空,
如果为空的时候抛出异常,否则调用UsernamePasswordAuthenticationToken进行授权操作

//3.从redis中获取用户信息
String rediskey = "login:"+userId;
LoginUser loginUser = redisCache.getCacheObject(rediskey);
if(Objects.isNull(loginUser))
{
   throw new RuntimeException("用户未登录");
}
//前面存入的是loginUser类型,因此这里不需要强转
//4.存入SecurityContextHolder,这里setAuthentication需要传入
//TODO 获取权限信息封装到Authentication之中
//Authentication类,因此loginUser不能直接放入进去,需要转换
//成Authentication类
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(loginUser,null,null);

(2)调用完之后返回接口的结果

如果是第一次调用完成之后(调用完/user/login接口之后),直接返回调用成功提示

Map<String,String> map = new HashMap<>();
map.put("token",jwt);
//把完整的用户信息存入redis,userId作为key,这里面的键值对键为token
//值为jwt
redisCache.setCacheObject("login:"+userid,loginUser);
//这里必须打开redis,才能够保存得上
return new ResponseResult(200,"登录成功",map);

如果是第二次调用完成之后(调用完/hello接口之后),返回hello的内容

@RestController
public class HelloController {
    //加入spring-security包会进入一个默认的拦截页面
    //前后端校验的关键:token,根据token来看是否是需要的用户,
    //登录后再访问其他请求是否需要在请求头中携带token内容
    @RequestMapping("/hello")
    public String hello(){
        System.out.println("!!!Hello!!!");
        return "hello";
    }
}

注意:/login为系统自带的默认接口,跟/user/login无关,只不过/login这个接口能够在浏览器上打开

注意:如果SecurityConfig.java中的configure函数中的http.addFilterBefore(jwtAuthenticationTokenFilter,UsernamePasswordAuthenticationFilter.class)忘记加上了之后,则登录完再去运行hello接口仍然无法调用,因为JwtAuthenticationTokenFilter extends OncePerRequestFilter这个接口会在运行完成所有Filter之后调用
运行过程当中出现的Filter
而在FilterSecurityInterceptor这一个拦截器中因为hello接口没有权限已经被拦截了,因此这里会报403 Forbidden的错误

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

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

相关文章

Ceph基础知识和基础架构认识

1 Ceph基础介绍 Ceph是一个可靠地、自动重均衡、自动恢复的分布式存储系统&#xff0c;根据场景划分可以将Ceph分为三大块&#xff0c;分别是对象存储、块设备存储和文件系统服务。在虚拟化领域里&#xff0c;比较常用到的是Ceph的块设备存储&#xff0c;比如在OpenStack项目…

鲁棒优化入门(6)—Matlab+Yalmip两阶段鲁棒优化通用编程指南(上)

0.引言 上一篇博客介绍了使用Yalmip工具箱求解单阶段鲁棒优化的方法。这篇文章将和大家一起继续研究如何使用Yalmip工具箱求解两阶段鲁棒优化(默认看到这篇博客时已经有一定的基础了&#xff0c;如果没有可以看看我专栏里的其他文章)。关于两阶段鲁棒优化与列与约束生成算法的原…

恒运资本:意外!房地产板块风云突变

今天上午&#xff0c;A股震动调整&#xff0c;半导体工业链走强&#xff0c;光刻胶、国家大基金持股、先进封装等板块涨幅居前。 房地产板块开盘小幅冲高后忽然大跳水&#xff0c;之后继续跌落&#xff0c;到上午收盘&#xff0c;板块内逾30只个股跌超5%。珠江股份、首开股份、…

2023年信息安全管理与评估(赛项)评分标准第三阶段夺旗挑战CTF(网络安全渗透)

全国职业院校技能大赛 高职组 信息安全管理与评估 &#xff08;赛项&#xff09; 评分标准 第三阶段 夺旗挑战CTF&#xff08;网络安全渗透&#xff09; 竞赛项目赛题 本文件为信息安全管理与评估项目竞赛-第三阶段赛题&#xff0c;内容包括&#xff1a;夺旗挑战CTF&#xff08…

京东通过RPA+智能问答,实现微信端智能客服

一 背景 由于业务发展迅速&#xff0c;服务的商家越来越多&#xff0c;目前京东的售后团队都是通过企业微信群和客户进行沟通&#xff0c;平时客户的相关问题也是在企业微信中来讨论解决&#xff1b; 但是售后团队资源有限&#xff0c;而且有的问题客户会重复问&#xff0c;周…

ATA-2022B高压放大器的电子实验案例(案例合集)

ATA-2022B是一款可放大交直流信号的双通道高压放大器。最大输出200Vp-p电压&#xff0c;输出电压范围可根据输出轨调节&#xff1b;直流偏置电压三档可调&#xff0c;连续变化最大可输出160V&#xff0c;可以实现输出非对称信号需求&#xff0c;驱动高压型负载。凭借其优异的指…

项目-IM

zk 启动类实现CommandLineRunner接口&#xff0c;重写run()方法 单聊 群聊 离线消息

uniapp 微信小程序仿抖音评论区功能,支持展开收起

最近需要写一个评论区功能&#xff0c;所以打算仿照抖音做一个评论功能&#xff0c;支持展开和收起&#xff0c; 首先我们需要对功能做一个拆解&#xff0c;评论区功能&#xff0c;两个模块&#xff0c;一个是发表评论模块&#xff0c;一个是评论展示区。接下来对这两个模块进行…

论文于祥读及复现——《Multi-level Map Construction for Dynamic Scenes》

论文祥读之——动态场景的多层次地图构建 0. 出发点&#xff08;暨摘要&#xff09;1. 引言2. 相关工作3.主要内容概括3.1 几何地图的构建3.1.1 密集点云地图和八叉图的构建3.1.2 平面地图的构建 3.2 对象地图的构建3.2.1 对象参数化和数据关联3.2.2 对象的更新与优化 4. 实验4…

数组——双指针法

双指针法 用两个同向或者反向的指针来代替两重循环。 提醒&#xff1a;不要老想着用同向双指针&#xff0c;有时候&#xff0c;相向双指针更容易解决问题。 LeetCode 27 class Solution {public int removeElement(int[] nums, int val) {int j0;for(int i0;i<nums.leng…

Hibernate(Spring-Data)3种实体继承创建表方式指南

文章目录 引言1. Hibernate 实体继承概述1.1 继承的概念和作用1.2 Hibernate 中的实体继承方式1.3 基础注解 2. 单表继承策略2.1 概述2.2 表结构设计2.3 实体类映射配置 3. 具体类继承策略3.1 概述3.2 表结构设计3.3 实体类映射配置 4. 映射超类策略(每个类一张表)4.1 概述4.2 …

vue3动态路由警告问题

{ path: "/:pathMatch(.*)*", // 必备 component: () > import("/views/error/404.vue"), }, 路由里添加

通达信接口查询持仓数据步骤(一)

股市里用到的查询通达信接口数据通常需要经历以下步骤&#xff1a; 1. 连接接口&#xff1a;首先需要使用相应的接口来连接到交易所或证券公司的服务器。这通常需要使用开发包或API提供的相关函数或方法进行连接。 2. 登录认证&#xff1a;在连接成功后&#xff0c;需要使用登录…

第八周第四天学习总结

测试linux基础并复习基础命令

含纽扣电池的产品出口澳洲需要做哪些认证?认证标准是什么?

澳大利亚含纽扣电池产品新规 01纽扣电池安全问题<<<< 在澳大利亚&#xff0c;已有儿童因为误食纽扣电池而导致死亡&#xff0c;且每月至少有一名儿童因吞咽或插入纽扣/硬币电池而严重受伤&#xff0c;导致其中一些儿童永久性损伤&#xff0c;而全世界数以百万计的…

一起来学shiny把(5)—反应式

什么是shiny&#xff1f;Shiny是一个R包&#xff0c;可让您轻松地直接从 R 构建交互式 Web 应用程序&#xff08;应用程序&#xff09;。本系列是个长教程&#xff0c;带你由浅入深学习shiny。 上一节我们在文章《R语言系列教程—–一起来学shiny吧&#xff08;4&#xff09;》…

《Go 语言第一课》课程学习笔记(十四)

接口 认识接口类型 接口类型是由 type 和 interface 关键字定义的一组方法集合&#xff0c;其中&#xff0c;方法集合唯一确定了这个接口类型所表示的接口。type MyInterface interface {M1(int) errorM2(io.Writer, ...string) }我们在接口类型的方法集合中声明的方法&#…

风力发电场集中监控系统解决方案

作为清洁能源之一&#xff0c;风力发电场近几年装机容量快速增长。8月17日&#xff0c;国家能源局发布1-7月份全国电力工业统计数据。截至7月底&#xff0c;全国累计发电装机容量约27.4亿千瓦&#xff0c;同比增长11.5%。其中&#xff0c;太阳能发电装机容量约4.9亿千瓦&#x…

有指针或者多维数组时,如何写 拷贝构造函数,移动构造函数,拷贝赋值运算符,移动赋值运算符

当成员变量里面有指针或者多维数组时&#xff0c;如何写 拷贝构造函数&#xff0c;移动构造函数&#xff0c;拷贝赋值运算符&#xff0c;移动赋值运算符 头文件 #pragma once #include<iostream> using namespace std; const int num 5; /* 重写C默认函数 */ class De…