Spring Security(3)

news2025/1/11 23:52:37

您好,我是湘王,这是我的CSDN博客,欢迎您来,欢迎您再来~

前面运行写好的代码之所以没有任何显示,是因为还没有对Spring Security进行配置,当然啥也不显示了。这就好比你坐在车上,却不打开发动机,车子当然跑不起来。所以咱们就来让它跑起来。不过在配置之前,有必要对Spring Security的登录流程做个大致了解。

如果深入源码去了解,这个玩意及其复杂,但是没必要,知道它的机制就行了。就好比你买车也不必把发动机拆开去看它是怎么工作的吧。简单来说它就是下面这些步骤:

1、Spring Security通过AuthenticationManager接口进行身份验证

2、ProviderManager是AuthenticationManager的一个默认实现

3、ProviderManager把验证工作委托给了AuthenticationProvider接口

4、AuthenticationProvider的实现类DaoAuthenticationProvider会检查身份认证

5、DaoAuthenticationProvider又把认证工作委托给了UserDetailsService接口

6、自定义UserDetailsService类从数据库中获取用户账号、密码、角色等信息,然后封装成UserDetails返回

7、使用Spring Security还需要自定义AuthenticationProvider接口,获取用户输入的账号、密码等信息,并封装成Authentication接口

8、将UserDetails和Authentication进行比对,如果一致就返回UsernamePasswordAuthenticationToken,否则抛出异常

下面是认证流程图:

 

首先重写loadUserByUsername:

/**
 * 自定义用户详情
 *
 * @author 湘王
 */
@Service("userDetailsService")
public class CustomUserDetailsService implements UserDetailsService {
    @Autowired
    private UserService userService;
    @Autowired
    private RoleService roleService;
    @Autowired
    private UserRoleService userRoleService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        Collection<GrantedAuthority> authorities = new ArrayList<>();
        // 从数据库中取出用户信息
        SysUser user = userService.getByName(username);
        // 判断用户是否存在
        if(null == user) {
            System.out.println("user is not exist");
            throw new UsernameNotFoundException("user is not exist");
        }

        // 获得用户角色:方式一
        List<SysUserRole> list = userRoleService.getByUserId(user.getId());
//        // 获得用户角色:方式二
//        List<SysRole> list = roleService.getByUserId(user.getId());

//        // 给用户添加授权:方式一
//        for (SysUserRole userRole : list) {
//            SysRole role = roleService.getById(userRole.getRoleid());
//            authorities.add(new SimpleGrantedAuthority(role.getName()));
//        }
//        // 返回UserDetails实现类
//        return new User(user.getName(), user.getPassword(), authorities);

        // 给用户添加授权:方式二
        return User
                .withUsername(username)
                .password(user.getPassword())
                .authorities(list.stream()
                                    .filter(Objects::nonNull)// 判断是否为空
                                    .map(userRole -> roleService.getById(userRole.getRoleid()))// 从SysUserRole获取Role
                                    .map(SysRole::getName)// 转变为角色名称字符串
                                    .map(SimpleGrantedAuthority::new)// 依据角色名称创建SimpleGrantedAuthority
                                    .toArray(SimpleGrantedAuthority[]::new)// list转变为数组
                ).build();
    }
}

因为UserDetailsService返回了封装的UserDetails,所以需要再自定义AuthenticationProvider返回Authentication接口:

/**
 * 自定义登录验证
 *
 * @author 湘王
 */
@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
    @Autowired
    private CustomUserDetailsService customUserDetailsService;

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        // 获取表单输入中返回的用户名
        String username = (String) authentication.getPrincipal();
        // 获取表单中输入的密码
        String password = (String) authentication.getCredentials();
        // 这里调用我们的自己写的获取用户的方法
        UserDetails userInfo = customUserDetailsService.loadUserByUsername(username);
        if (userInfo == null) {
            System.out.println("user is not exist");
            throw new UsernameNotFoundException("user is not exist");
        }

        PasswordEncoder passwordEncoder = new PasswordEncoder() {
            @Override
            public String encode(CharSequence charSequence) {
                return charSequence.toString();
            }
            @Override
            public boolean matches(CharSequence charSequence, String s) {
                return s.equals(charSequence.toString());
            }
        };

        // 采用简单密码验证
        if (!passwordEncoder.matches(password, userInfo.getPassword())) {
            System.out.println("user or password error");
            throw new BadCredentialsException("user or password error");
        }

        Collection<? extends GrantedAuthority> authorities = userInfo.getAuthorities();
        // 构建返回的用户登录成功的token
        return new UsernamePasswordAuthenticationToken(userInfo, password, authorities);
    }

    @Override
    public boolean supports(Class<?> aClass) {
        return true;
    }
}

接着来实现实现WebSecurityConfigurerAdapter,它通过重写WebSecurityConfigurerAdapter中的相关方法(一般是configurer)来自定义配置。WebSecurityConfigurerAdapter主要做几件事:

1、初始化

2、开启Security

3、配置各种过滤器,实现验证过滤器链

下面是它的代码:

/**
 * spring security验证配置
 *
 * @author 湘王
 */
// 配置类
@Configuration
// 开启Security服务
@EnableWebSecurity
// 开启全局Securtiy注解
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
	@Autowired
	private CustomUserDetailsService customUserDetailsService;
	@Autowired
	private CustomAuthenticationProvider authenticationProvider;

	// 自定义的登录验证逻辑
	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		auth.authenticationProvider(authenticationProvider);
	}
	// 控制逻辑
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		// 执行UsernamePasswordAuthenticationFilter之前添加拦截过滤
		http.addFilterBefore(new CustomInterceptorFilter(), UsernamePasswordAuthenticationFilter.class);

		http.authorizeRequests()
			.anyRequest().authenticated()
			// 设置自定义认证成功、失败及登出处理器
			.and().formLogin().loginPage("/login")
			.and().cors()
			.and().csrf().disable();
	}
	@Override
	public void configure(WebSecurity web) throws Exception {
		// 设置拦截忽略文件夹,可以对静态资源放行
		web.ignoring().antMatchers("/css/**", "/js/**");
	}
}

接着用postman进行测试:

 

回顾整个调用过程,它的时序图是:

 

但是等等:好像除了/login,其他方法都不能正常访问!


感谢您的大驾光临!咨询技术、产品、运营和管理相关问题,请关注后留言。欢迎骚扰,不胜荣幸~

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

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

相关文章

智慧灾备解决方案-最新全套文件

智慧灾备解决方案-最新全套文件一、建设背景二、建设思路三、建设方案四、获取 - 智慧灾备全套最新解决方案合集一、建设背景 随着“云物移大智”各种技术的飞速发展&#xff0c;信息系统种类越来越多&#xff0c;数据保护也备受关注&#xff0c;传统数据保护往往仅覆盖数据库…

(CVPR 2019) 3D-SIS: 3D Semantic Instance Segmentation of RGB-D Scans

图 1&#xff1a;3D-SIS对RGB-D扫描数据执行3D实例分割&#xff0c;学习将2D RGB输入特征与3D扫描几何特征联合融合。结合能够在测试时对全3D扫描进行推理的全卷积方法&#xff0c;我们实现了对目标边界框、类标签和实例掩码的准确推理。 Abstract 我们介绍了3D-SIS&#xff0…

编译原理实验--实验三 预测分析法判断算术表达式的正确性--Python实现

目录 一、实验目的和要求 二、实验内容 三、实验环境 四、实验步骤 1、语法分析所依据的文法&#xff1b; 2、给出消除左递归及提取左公因子的LL(1)文法&#xff1b; 3、预测分析表 4、关键代码 五、实验结果与分析 一、实验目的和要求 理解自顶向下语法分析方法&#…

线程是什么?线程的相关概念以及基本的使用方法说明【内附可执行源码注释完整】

文章目录❓线程是什么&#x1f680;为什么要在程序中使用线程&#x1f34e;线程的优点、缺点&#x1f382;线程的应用场合&#x1f330;线程的基本使用⭐创建线程⭐线程的终止⭐等待指定线程结束⭐线程程序的编译命令&#x1f3e0;线程使用案例❓线程是什么 首先我们要知道进程…

3.1 首页功能的开发-跳转到首页

第三章. 业务功能开发 项目结构如下&#xff1a; 3.1 首页功能的开发 用户访问项目首页&#xff0c;首先进入登录页面。 用户登录流程图&#xff1a; 我们先写跳转到登录界面&#xff0c;在请求准发到index中如下图所示&#xff1a; 由于配置了视图解析器&#xff0c;查看ind…

GaussDB修改表空间占用磁盘使用率阈值

GaussDB数据库表空间占用磁盘使用率达到了你所设定的值后数据库会进入只读模式 执行非只读SQL时报错如下&#xff1a; ERROR: cannot execute %s in a read-only transaction. 或者运行中部分非只读SQL&#xff08;insert、update、create table as、create index、alter tab…

MCE | 促炎症的 PCD——细胞焦亡

程序性细胞死亡 (Programmed cell death, PCD) 是多细胞生物中&#xff0c;由基因调控的细胞自杀过程&#xff0c;对多细胞生物的发育、体内稳态和完整性至关重要。PCD 的研究涉及多个领域&#xff0c;如免疫、神经系统发育、癌症、感染等。常见的 PCD 有细胞凋亡 (Apoptosis)、…

贪吃蛇-第12届蓝桥杯Scratch选拔赛真题精选

[导读]&#xff1a;超平老师计划推出Scratch蓝桥杯真题解析100讲&#xff0c;这是超平老师解读Scratch蓝桥真题系列的第83讲。 蓝桥杯选拔赛每一届都要举行4~5次&#xff0c;和省赛、国赛相比&#xff0c;题目要简单不少&#xff0c;再加上篇幅有限&#xff0c;因此我精挑细选…

通信基石Socket结合OOP实现程序间的通信

学习目录前言一.Socket是用来干什么的二.如何用代码建立通信连接三.Java实现TCP发收消息四.UDP的Socket编程前言 先分享一下最近看到的几篇面经&#xff1a; 看了一下关于Socket的知识点好像面试十分高频&#xff0c;它作为通信的基石许多组件&#xff0c;框架都是在他的基础…

oracle数据库id字段自增长

mysql数据库中建表的时候可以设置id字段自增长&#xff0c;oracle数据库中要实现id字段自增长需要借助于sequence&#xff08;序列&#xff09;和trigger&#xff08;触发器&#xff09;。 下面通过一个简单的示范说明。 --1.新建一个账户表 account create table accout( …

机械转码日记【25】多态

目录 前言 1.多态的概念 2. 多态的定义及实现 2.1多态的构成条件 2.2 虚函数 2.3虚函数的重写 2.4虚函数重写的两个例外 2.4 C11 override 和 final 2.5 重载、覆盖(重写)、隐藏(重定义)的对比 3.抽象类 3.1概念 3.2 接口继承和实现继承 4.多态的原理 4.1虚函…

个人微信api

我们是一家专业服务企业数字化微信管理服务的技术服务团队&#xff0c;服务于需求SCRM、机器人、营销系统、社群小助手等具有研发能力的企业,同时我们也接收因使用Xp方案、ipad方案、PC方案导致被批量封号的企业&#xff0c;我们合作伙伴目前包含&#xff1a;金融服务行业Top10…

无协同资源创新打法,这几个品牌在双11「品牌嘉年华」实现品效双收

抖音双11好物节圆满收官&#xff0c;每年双11&#xff0c;我们关注的不仅仅是不断刷新的成绩&#xff0c;也是在更多元的场景、更丰厚的资源和更强劲的平台资源助力之下&#xff0c;商家在营销动作上带来了怎样的“惊喜”。 在「内容场景」&#xff0c;双11期间&#xff0c;抖…

VScode设置pretty-printer无效

文章目录VScode设置pretty-printer无效问题解决尝试正式解决参考VScode设置pretty-printer无效 问题 win10系统下&#xff0c;VScode中即使在launch.json中进行了如下设置&#xff0c;还是无效 {"description": "为 gdb 启用整齐打印","text"…

【设计模式】 - 结构型模式 - 外观模式

目录标题前言外观模式概述结构实现&#xff1a;智能家电控制优缺点前言 结构型模式描述如何将类或对象按某种布局组成更大的结构。它分为类结构型模式和对象结构型模式&#xff0c;前者采用继承机制来组织接口和类&#xff0c;后者釆用组合或聚合来组合对象。 由于组合关系或聚…

基于机器学习的自动音乐生成播放器

目录 详细设计说明书 1 1 引言 1 1.1 编写目的 1 1.2 背景 1 2. 此项目的任务提出者&#xff1a;西电软件工程课程组、西电软件开发小组 1 1.3 定义 2 1.4 参考资料 2 3 程序描述 4 3.1 011 参数调整模块 4 3.1.3 界面设计 4 3.1.4 参数调整的内部逻辑 5 3.2 012 自动谱曲模块 …

webpack 的基本使用(详解)

前言&#xff1a; 你是否也是只会运用框架中集成好的Webpack配置呢&#xff1f;你明白每一项的意义么&#xff1f;你懂多少Webpack的个性化配置项呢&#xff1f;本篇文章为你讲解Webpack中的各种配置项参数及作用&#xff01; 目录前言&#xff1a;一&#xff0c;什么是Webpack…

【算法手札】深入理解宽度遍历(bfs)和深度遍历(dfs)搜索

算法的重要性不言而喻&#xff0c;现在我们的生活也已经离不开各种算法&#xff0c;一个好的算法能大大提高程序的运行效率&#xff0c;是学习编程的一个重要模块&#xff0c;而遍历算法也是算法里的一个大的模块&#xff0c;今天我们一起来学习一下深度遍历算法&#xff08;de…

2023最新SSM计算机毕业设计选题大全(附源码+LW)之java电子书店管理系统ya226

做毕业设计一定要选好题目。毕设想简单&#xff0c;其实很简单。这里给几点建议&#xff1a; 1&#xff1a;首先&#xff0c;学会收集整理&#xff0c;年年专业都一样&#xff0c;岁岁毕业人不同。很多人在做毕业设计的时候&#xff0c;都犯了一个错误&#xff0c;那就是不借鉴…

双功能接头试剂知识分享:Alkyne maleimide,Mal-Alkyne,炔烃-马来酰亚胺

炔烃马来酰亚胺是一种双功能接头试剂&#xff0c;可将末端炔烃连接到各种含硫醇分子&#xff0c;例如含有半胱氨酸残基的蛋白质。然后可以通过铜催化的点击化学反应将炔烃部分与各种叠氮化物缀合。 Alkyne maleimide is a bifunctional connector reagent that can connect ter…