springsecurity 使用浅谈(一)
1. 背景
springsecurity框架主要用于Web应用的认证和授权。所谓认证就是验证当前访问系统的是不是本系统的用户,并且要确认具体是哪个用户。而授权就是经过认证后判断当前用户是否有权
限进行某个操作。认证和授权也是SpringSecurity作为安全框架的核心功能。
2. 前置知识
在传统的项目中,我们通常使用会话技术来保存用户信息并进行用户认证。会话是客户端和服务器之间连续的请求和响应过程,用于保持用户的状态信息。然而,HTTP协议本身是无状态的,服务器无法直接识别每个请求的来源,因此需要一种机制来标识和关联请求。
在会话管理中,有两种常见的实现方式:session(会话)和cookie(Cookie)。Session通过在服务器端记录信息来确定用户身份和状态,但这会增加服务器的存储压力。当客户端浏览器访问服务器时,服务器会在服务器端创建一个会话,并为该会话分配一个唯一的标识符,即sessionID。服务器将sessionID发送给客户端浏览器,通常通过设置一个名为"sessionID"的Cookie,将其存储在浏览器中。
一旦客户端浏览器保存了该Cookie,它会在后续的请求中自动发送给服务器,从而实现会话的持续。服务器通过sessionID可以在服务器端找到对应的会话数据,并根据其中的用户状态进行处理。如果浏览器禁用了Cookie,也可以通过URL重写的方式将sessionID作为查询参数发送给服务器。
另一方面,Cookie是服务器在HTTP响应中附带的一个小型文本文件,它存储在客户端浏览器中。一旦浏览器保存了某个Cookie,它会在后续的请求中自动将该Cookie发送给服务器。通过在Cookie中存储一些用户标识或其他数据,服务器可以在不使用session的情况下实现状态保持和用户认证。
最近,使用基于令牌(token)的身份验证方式逐渐流行起来,逐渐取代了传统的session和cookie方式。令牌是一种加密的字符串,它包含了与用户相关的信息,并可以通过在每个请求的头部或参数中携带令牌来实现身份验证。与session和cookie不同,令牌不需要在服务器端存储用户状态,这降低了服务器的存储压力,并且可以更好地支持分布式系统和跨域访问。
总结一下,传统的会话管理中使用了session和cookie来实现用户认证和状态保持。Session通过在服务器端记录信息,而cookie则是在客户端浏览器中存储会话标识符。然而,随着基于令牌的身份验证方式的兴起,令牌逐渐取代了session和cookie,提供了更灵活和可扩展的身份验证机制。令牌通过在请求中携带加密的字符串来验证用户身份,不需要在服务器端存储状态信息,从而提供了更好的性能和安全性。
Session的弊端包括以下几点:
服务器压力增大:通常情况下,Session是存储在服务器的内存中的,每个用户认证通过后,其Session数据都会保存在服务器的内存中。当用户数量增大时,服务器的内存压力也会增大。
CSRF跨站伪造请求攻击:Session通常是基于Cookie进行用户识别的,如果Cookie被截获,用户容易受到跨站请求伪造(CSRF)攻击。即使不使用Cookie,而是通过URL重写方式发送SessionID,也容易被截获敏感信息。
扩展性不强:在多个服务器部署的情况下,如果用户登录到一台服务器A上,而下次访问另一台服务器B,B上并没有A的Session信息。为了解决这个问题,需要将Session保存到数据库中,这增加了服务器的存储压力。
相比之下,Token是一种令牌,由服务端生成的加密字符串,作为客户端请求的标识。当用户登录后,服务器生成一个Token并返回给客户端浏览器,以后客户端只需携带这个Token进行请求即可,无需再次携带用户名和密码。
浏览器将接收到的Token存储在本地存储(Local Storage)中。当浏览器再次访问服务器时,服务器对浏览器传来的Token进行解密,并进行用户数据查询。如果查询成功,则认证通过,实现状态保持。因此,即使有多台服务器,服务器只需解密Token和查询用户数据,而无需在服务器端保存用户的认证信息或会话信息。这就为应用的扩展提供了便利,同时也减少了服务器的存储压力。Token的出现解决了Session的弊端,成为Session的替代品。
JWT
JWT其实就是一种被广泛使用的token,它的全称是JSON Web Token,它通过数字签名的方式,以JSON对象为载体,在不同的服务终端之间安全地传输信息。
JWT最常见的使用场景就是授权认证,一旦用户登录,后续每个请求都将包含JWT,系统在每次处理用户请求之前,都要先进行JWT安全校验,通过之后再进行处理。
JWT由三部分组成,它们通过点号(.)进行分隔:
Header(头部):包含了令牌的类型(即JWT)和所使用的签名算法(例如HMAC、RSA等)。
示例:{"alg": "HS256", "typ": "JWT"}Payload(负载):包含了一些声明信息,用于在令牌中传递有关用户或其他实体的信息。声明分为三种类型:注册声明(Registered Claims)、公共声明(Public Claims)和私有声明(Private Claims)。注册声明是一组预定义的声明,例如iss(签发者)、exp(过期时间)等。公共声明是自定义的声明,但建议遵循一些已定义的命名规范。私有声明是用户自定义的声明,用于满足应用程序的特定需求。
示例:{"sub": "user123", "name": "John Doe", "admin": true}Signature(签名):使用密钥对Header和Payload进行签名,以验证令牌的完整性和真实性。签名可以防止令牌被篡改或伪造。签名通常使用密钥和指定的签名算法进行计算。
示例:HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
JWT的工作流程如下:
用户通过提供有效的凭据(如用户名和密码)进行认证。
服务器验证凭据,并生成一个JWT。
服务器将JWT作为响应的一部分返回给客户端。
客户端将JWT保存在本地,通常存储在Local Storage或Session Storage中。
客户端在后续的请求中将JWT作为身份验证凭据进行发送。
服务器接收到请求后,通过验证JWT的签名和有效性来验证用户身份。
如果JWT验证成功,服务器使用其中的信息来处理请求。
JWT具有以下优点:
无需在服务器端存储会话信息:JWT是基于令牌的认证机制,服务器不需要在存储中保存会话信息,从而提高了可扩展性。
跨域支持:由于JWT是通过在请求中携带令牌进行验证,因此可以轻松支持跨域请求。
自包含性:JWT中包含了所有必要的信息,因此服务器不需要在数据库中进行查询,从而提高了性能。
可扩展性:JWT的负载中可以包含自定义的声明,以满足应用程序的特定需求。
3. springsecurity原理
Spring Security的核心原理是基于过滤器链(Filter Chain)的工作机制。当一个请求进入应用程序时,Spring Security会通过一系列的过滤器来处理该请求,进行认证和授权操作。
下面是Spring Security的一般工作流程:

客户端发送请求到应用程序,请求被Spring Security的过滤器链拦截。
过滤器链中的第一个过滤器是
UsernamePasswordAuthenticationFilter,它负责处理基于用户名和密码的认证请求。如果请求携带了用户名和密码,该过滤器将尝试对用户进行认证。认证过程中,Spring Security使用
AuthenticationManager来进行实际的认证操作。AuthenticationManager是一个接口,用于管理不同的认证提供者(Authentication Provider)。认证提供者根据请求中的用户名和密码,从用户存储(如数据库)中获取用户信息,并进行密码匹配验证。如果验证成功,认证提供者将返回一个
Authentication对象,其中包含了用户的身份信息和权限信息。如果认证成功,
UsernamePasswordAuthenticationFilter会调用AuthenticationSuccessHandler来处理认证成功的逻辑,例如生成并返回一个令牌(如JWT),或者重定向到某个页面。如果认证失败,
UsernamePasswordAuthenticationFilter会调用AuthenticationFailureHandler来处理认证失败的逻辑,例如返回一个错误消息或重定向到登录页面。认证成功后,请求继续经过过滤器链的其他过滤器,例如
CsrfFilter用于防止跨站请求伪造攻击,CorsFilter用于处理跨域资源共享。在请求到达受保护的资源时,Spring Security会使用
AccessDecisionManager来进行授权判断。AccessDecisionManager是一个接口,用于根据用户的身份和权限信息,决定是否允许访问该资源。如果用户被授权访问资源,请求将被传递到相应的控制器或处理器进行处理。
3. springsecurity 认证流程

在Spring Security中,认证流程涉及多个组件和步骤,主要包括以下几个关键步骤:
用户发送认证请求:用户在应用程序的登录页面输入用户名和密码,并发送认证请求。
过滤器链拦截请求:Spring Security的过滤器链拦截认证请求,拦截器链中的第一个过滤器是
UsernamePasswordAuthenticationFilter,它负责处理基于用户名和密码的认证请求。AuthenticationManager处理认证:
UsernamePasswordAuthenticationFilter将认证请求交给AuthenticationManager处理。AuthenticationManager是Spring Security的认证管理器,负责实际的认证操作。获取用户信息:认证管理器使用
UserDetailsService来获取用户信息。UserDetailsService是一个接口,定义了获取用户信息的方法。它可以从数据库、LDAP、内存或其他数据源中获取用户信息。构建Authentication对象:认证管理器根据获取到的用户信息,构建一个
Authentication对象,其中包含了用户的身份信息和权限信息。进行密码匹配验证:认证管理器会使用
PasswordEncoder接口来进行密码匹配验证。PasswordEncoder是一个接口,定义了密码加密和验证的方法。它用于比较用户输入的密码和存储的密码是否匹配。认证成功:如果密码匹配验证成功,认证管理器将返回一个经过认证的
Authentication对象。AuthenticationProvider处理认证结果:认证管理器会将认证结果传递给
AuthenticationProvider进行进一步处理。AuthenticationProvider是一个接口,定义了对认证结果的处理方法。调用认证成功处理器或认证失败处理器:根据认证结果,
AuthenticationProvider将调用相应的认证成功处理器(AuthenticationSuccessHandler)或认证失败处理器(AuthenticationFailureHandler)。认证成功处理:如果认证成功,认证成功处理器将根据配置的策略执行相应的操作,如生成并返回令牌(如JWT)或重定向到指定页面。
认证失败处理:如果认证失败,认证失败处理器将根据配置的策略执行相应的操作,如返回错误消息或重定向到登录页面。
4. springsecurity 自定义实现思路
当访问某个业务接口时,会被Security的login接口拦截,但是如果我们不想使用Security默认的登录页面,那么怎么办呢,还有,springsecurity的校验,默认是从内存中查找,而我们希望是根据数据库来做校验,那么怎么实现呢。根据上边的认证流程图可知,我们需要从不满足我们需求的地方去重新实现其方法,按照我们需求实现:
【登录】
①、自定义登录接口。然后自己去调用ProviderManager的方法进行认证 如果认证通过生成jwt,然后把用户信息存入redis中,从而实现无状态的会话管理和快速的用户信息访问。
②、自定义UserDetailsService接口的实现类。在这个实现类中自己重写相应方法,方法去查询数据库,来做校验
【校验】
①、定义Jwt认证过滤器。用于获取token,然后解析token获取其中的userid,还需要从redis中获取用户信息,然后存入SecurityContextHolder。SecurityContextHolder是Spring Security提供的一个类,用于管理当前用户的安全上下文信息。它主要用于在整个请求处理过程中存储和获取当前认证的用户信息
springsecurity 使用浅谈(一)的更多相关文章
- 浅谈使用spring security中的BCryptPasswordEncoder方法对密码进行加密与密码匹配
浅谈使用springsecurity中的BCryptPasswordEncoder方法对密码进行加密(encode)与密码匹配(matches) spring security中的BCryptPass ...
- 浅谈 Fragment 生命周期
版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 源码:AndroidDemo/Fragment 文中如有纰漏,欢迎大家留言指出. Fragment 是在 Android 3.0 中 ...
- 浅谈 LayoutInflater
浅谈 LayoutInflater 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 源码:AndroidDemo/View 文中如有纰漏,欢迎大家留言指出. 在 Android 的 ...
- 浅谈Java的throw与throws
转载:http://blog.csdn.net/luoweifu/article/details/10721543 我进行了一些加工,不是本人原创但比原博主要更完善~ 浅谈Java异常 以前虽然知道一 ...
- 浅谈SQL注入风险 - 一个Login拿下Server
前两天,带着学生们学习了简单的ASP.NET MVC,通过ADO.NET方式连接数据库,实现增删改查. 可能有一部分学生提前预习过,在我写登录SQL的时候,他们鄙视我说:“老师你这SQL有注入,随便都 ...
- 浅谈WebService的版本兼容性设计
在现在大型的项目或者软件开发中,一般都会有很多种终端, PC端比如Winform.WebForm,移动端,比如各种Native客户端(iOS, Android, WP),Html5等,我们要满足以上所 ...
- 浅谈angular2+ionic2
浅谈angular2+ionic2 前言: 不要用angular的语法去写angular2,有人说二者就像Java和JavaScript的区别. 1. 项目所用:angular2+ionic2 ...
- iOS开发之浅谈MVVM的架构设计与团队协作
今天写这篇博客是想达到抛砖引玉的作用,想与大家交流一下思想,相互学习,博文中有不足之处还望大家批评指正.本篇博客的内容沿袭以往博客的风格,也是以干货为主,偶尔扯扯咸蛋(哈哈~不好好工作又开始发表博客啦 ...
- Linux特殊符号浅谈
Linux特殊字符浅谈 我们经常跟键盘上面那些特殊符号比如(?.!.~...)打交道,其实在Linux有其独特的含义,大致可以分为三类:Linux特殊符号.通配符.正则表达式. Linux特殊符号又可 ...
- 浅谈Angular的 $q, defer, promise
浅谈Angular的 $q, defer, promise 时间 2016-01-13 00:28:00 博客园-原创精华区 原文 http://www.cnblogs.com/big-snow/ ...
随机推荐
- Record -「Tricks」记录
曼哈顿距离 \(\text{dist}(A,B)=|x_{A}-x_{B}|+|y_{A}-y_{B}|\) 可以拆成 \(\max\{x_{A}-x_{B}+y_{A}-y_{B},x_{A}-x_ ...
- TCP vs UDP:揭秘可靠性与效率之争
概述 今天我们开始主要讲解TCP的相关知识点.在之前讲解分层章节的时候,我们提到过一个重要观点.在网络层及以下几层,更多的是让主机与主机建立连接,也就是说你的电脑需要知道另一台电脑在哪里才能连接上它. ...
- linux安装clickhouse
linux安装clickhouse 1. 系统要求 ClickHouse可以在任何具有x86_64,AArch64或PowerPC64LE CPU架构的Linux,FreeBSD或Mac OS X上运 ...
- AcWing 第102场周赛 题解
第一次ak周赛,写篇题解纪念一下 第一题 给定两个长度为 n n n 的整数序列 a 1 , a 2 , - , a n a_1,a_2,-,a_n a1,a2,-,an 以及 b 1 , b ...
- P6066 [USACO05JAN] Watchcow S
prologue 这个题这么水的一个板子题. analysis 这个题目我们正反建两条边,在跑欧拉回路的时候,看这个边是不是被走过,走过就不走,跳过这个边.如果没走,就走这条边并且标记这个边走过了. ...
- Centos7下创建centos-home逻辑分区
1备份要挂载的文件夹 查看home文件夹有无文件,如有文件一定要记得备份 2创建逻辑分区 2.1查看已有逻辑分区 2.2查看磁盘分区情况 2.3查看磁盘PV 2.4创建逻辑分区 lvcreate -n ...
- Linux第一次周总结
第一章 初识 Linux 1.Linux简介 Linux,一般指GNU/Linux(单独的Linux内核并不可直接使用,一般搭配GNU套件,故得此称呼),是一种免费使用和自由传播的类UNIX操作系统 ...
- 2023-10-25:用go语言,假如某公司目前推出了N个在售的金融产品(1<=N<=100) 对于张三,用ai表示他购买了ai(0<=ai<=10^4)份额的第i个产品(1<=i<=N) 现给出K(
2023-10-25:用go语言,假如某公司目前推出了N个在售的金融产品(1<=N<=100) 对于张三,用ai表示他购买了ai(0<=ai<=10^4)份额的第i个产品(1& ...
- ansible-配置文件优化-性能调优
ansible-配置文件详解:ansible默认配置文件为/etc/ansible/ansible.cfg,配置文件中可以对ansible进行各项参数的调整,包括并发线程.用户.模块路径.配置优化等, ...
- np.array和np.ndarry 的区别
np.array和np.ndarray都是NumPy中用于创建多维数组的函数. np.ndarray是NumPy中的多维数组类,它是一种可变的数组,可以通过修改数组中的元素来改变其内容.使用np.nd ...