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提供的一个类,用于管理当前用户的安全上下文信息。它主要用于在整个请求处理过程中存储和获取当前认证的用户信息
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net
机房租用,北京机房租用,IDC机房托管, http://www.fwqtg.net
近期,由中国信息通信研究院(以下简称中国信通院)、中国通信标准化协会指导,中国通信标准化协会大数据技术标准推进委员会(CCSA TC601)、InfoQ 极客传媒联合主办的 2023 可信数据库发展大会在北京圆满落幕。中国信通院正式公布了第 16 批可信数据库…