天天看点

【spring security学习笔记】身份验证方式session-cookie、jwt、OAuth的学习session cookieJWTOAuth 2.0

  • session cookie
    • 使用Spring Session管理Session
    • session并发配置
    • 强制下线
  • JWT
    • jwt的组成
    • JWT工作流程
  • OAuth 2.0

session cookie

介绍:

Web应用开发大多是基于HTTP协议的。客户端与服务端在使用HTTP协议进行交互时,协议本身并不会记录客户端的信息或是状态。这意味着HTTP协议是一个“无状态”协议,而Web应用中的很多场景是需要保存用户状态的。网站的安全性解决方案就需要对状态进行验证,访问一个受保护的资源的第一步便是检查用户是否通过登录验证。这样的情况催生了许多用于保存用户状态的解决方案,Session-Cookie就是其中最为常见的一种。

Session-Cookie由Session与Cookie两部分组成,分别存储于服务端和客户端。Session是一种用于记录用户的状态信息,被存储于服务端的数据。例如当前用户是否登录,身份与权限是怎样的,这类数据大多会存储于Session当中。 Cookie(或者被称作HTTP Cookie)则是一小片被存储在浏览器的数据,往往在浏览一个网站时便会产生。本身作用在于可以在访问网站的时候,记录一些状态信息到客户端。可以起到提升系统性能并且提高用户体验。在Session-Cookie体系中,Cookie主要用于记录一串与Session关联的标识。该标识往往被称作“session-id”。 Session与Cookie不同的是,Cookie是一个真实存在的具体实现,而Session是一个相对抽象的概念,不同的语言与开发框架对Session有不同的实现方式。这两者共同作用,使得用户即便是在使用“无状态”协议与服务端交互,依旧能在不同页面共享一些信息。流程如下:

· 客户发送请求到服务端。

· 服务端收到请求之后便会在内部创建Session,之后返回响应。在响应头中,包含Set-Cookie信息,其中内容就包含Session-id。

· 当客户端收到包含Set-Cookie的响应之后,后续的所有请求就会带上Cookie。

· 服务端可以根据Cookie中的Session-id让每个请求与Session逐一对应,实现请求间的状态共享。

使用Spring Session管理Session

在Spring Boot开发中,Session-Cookie默认由Web容器(例如Tomcat)维护。例如,访问一个Web容器是Tomcat的Spring Boot应用,默认返回的响应头中会设置一条键为JSESSIONID的Cookie

Session默认情况下是保存于服务端的JVM内存当中。在该条件下当服务端程序重启时,Session将会丢失。这也意味着用户会集体掉线,如果要继续操作则需要重新登录。在某些场景下,该情况会严重影响用户的使用体验。这不得不将Session的管理从JVM内存中剥离出来。 Spring Session便提供了一套方案用于解决Session管理的问题,使其不依赖于特定的应用程序容器实现Session集群化。下面将讲解Spring Boot整合Spring Session的步骤。

(1)更新依赖

整合Spring Session的第一个步骤是更新依赖。Spring Session可选的容器类型有四种,分别是Redis、MongoDB、JDBC和HAZELCAST。选择好对应的容器类型后,再根据类型选择对应的依赖项。比如选择Redis作为Session的外部容器,则需要引入spring-session-data-redis。

(2)修改配置

在application.properties中:

spring:

session:

store-type: redis

得益于Spring Boot的自动配置,加上该配置项之后等同于使用了注解@EnableRedisHttpSession。这将创建名为springSessionRepositoryFilter的过滤器。该过滤器负责将容器中的HttpSession替换为Spring Session。

(3)配置Redis连接

redis:

#redis域名

hostname: localhost

#redis端口

port: 6379

#redis密码

password: NlzWZLvvCF5Gzzby

(4)检验结果 启动程序,并且成功登录之后,可以通过Redis的GUI管理工具查看对应命名空间下的存储情况

修改SecurityConfig.java

session并发配置

Spring Security中默认对session的并发数并没有限制。换句话说,一个用户的账号和密码在默认条件下可以供任意数量的客户端登录。这种情况在某些场景下是难以接受的,例如,一些付费的视频,对于同账户的客户端在线数量会进行限制。Spring Security对于这样的场景也提供了支持。

首先定义一个SessionInformationExpiredStrategy的实现作为失效策略,用于返回异常信息。ParallelismSessionExpiredStrategy.java:

@Configuration

public class Parallelismsessionexpiredstrategy implements
SessioninformationexpiredStrategy {
private final ObjectMapper objectMapper;
public void onExpiredSessionDetected(Sessioninformationexpiredevent event)throws Ioexception, Servletexception{
//返回异常信息
event. getresponse(). setContentType("application/ison; charset=utf-8");
event. getresponse().getwriter(). write(objectMapper. writevalueasstring(R.failed("达到并发上限")));
}
}



           

SecurityConfig中加上sessionManagement配置,SecurityConfig.java:

@Configuration
@RequiredArgsConstructor
@EnableGlobalMethodSecurity(prePostEnabled = true,secureEnabled = true,jsr250Enabled = true)
public class Securityconfig extends WebSecurityConfigurerAdapter {
@override
protectedvoidconfigure(httpsecurityhttp)throws Exception{
http
//…
.sessionmanagement()//并发 Session上限为1
.maximumsessions(1)//达到上限后是否阻止登录
.maxSessionsPreventslogin( true)
//失效 Session策略
.expiredSessionStrategy(new
ParallelismSessionExpiredstrategy(mapper));//,' 
           

其中,maxSessionsPreventsLogin用于控制达到Session并发上限后的策略,True代表将阻止后续的登录操作,False代表将会使之前登录的Session强制失效。另外,失效Session的请求结果将按照expiredSessionStrategy中配置的策略进行返回。

强制下线

Session-Cookie的特点在于服务端可以监听并控制用户的会话状态。这为会话管理模块的开发提供了很大便利。会话管理中强制下线会是一个常见需求,如果一个Web应用中出现违规操作,可以使用强制下线功能为系统提供保护。

@Component
@RequiredArgsConstructor
public class SessionUtils{
private final SessionRegistry sessionRegistry;
public void expireUserSessions(String username){
for (Object principal:sessionRegistry. getAllPrincipals()){
if (principal instanceof SecurityProperties. User){
UserDetails userDetails =(UserDetails) principal;
//遍历 sessionRegistry中的 principal找到对应用户的 Session
if (userDetails. getUsername () equals (username)){
for (SessionInformation information:
sessionRegistry. getAllSessions (userDetails, true)){
//让 session立刻失效
information.expireNow(); 
}
}
}
}

           

需要某个用户强制下线时调用 SessionUtils的expireUserSessions()命令即可。

JWT

基于Session-Cookie模式的身份验证,这类模式的特点在于后端会维护用户的状态。在常见身份验证方式中,有一种方式与Session-Cookie有着鲜明差异,它不用于后端维护用户状态,这一方式正是将介绍的对象——JWT。

JWT是一个开放标准(RFC 7519),它定义了一种紧凑且自包含的方式,用于在各端之间将安全信息以JSON对象的形式进行传递。由于此信息是经过数字签名的,因此可以被验证和信任。另外,还可以使用HMAC或者RSA算法对JWT进行加密。

jwt的组成

JWT以紧凑的三部分组成,各部分以(.)分隔,形如xxxx.yyy.zzz。这三部分分别是:

· Header(标头)。通常由两部分组成,令牌的类型(即JWT)和所使用的签名算法,例如HMAC SHA256或RSA。

· Payload(负载)。负载中包含不同类型的声明(Claims)。有已注册声明(预定义声明,包含签发人、超时时间等信息)、公有声明和私有声明(由签发者用于在各端传递安全信息的声明,可随意定义)。

· Signature(签名)。基于标头中定义的加密算法进行加密的签名。例如,加密算法为HMACSHA256的话,签名过程将会是: 签名的作用在于保证消息在传输过程中是未被更改的,并且如果使用私钥进行签名,还可以对发送者身份进行进一步的认证。

基于JWT加密并且可携带安全信息的特点,后端可以将原本保存于Session中的信息保存至JWT的私有声明,然后要求客户端在每次请求中都携带JWT。 以这种方式实现后端服务的无状态化。

JWT带来后端无状态化的同时,也带来了不少好处:

(1)跨域与CORS 传统的认证方式中,Cookies是默认只能用于单个域名和子域名间进行消息传递。如果碰到域名不同的情况(也就是跨域),整个处理过程会变得繁琐。JWT不依赖Cookie,大多以Authorization:Bearer {JWT}这样的格式作为请求头进行传递,不会被跨域请求所影响。

(2)跨平台解决方案 同样是受限于Cookies的特性,移动平台与Cookies并不能很好地融合,存在诸多限制。JWT相比Cookie-Session更适合作为跨平台的认证解决方案。

(3)方便扩展,提升后端性能 Session-Cookie的验证过程中基本都需要对存储模块进行访问,特别在于分布式架构中的认证过程,后端服务需要对外部存储进行访问,其中必然存在数据传输带来的时间损耗。JWT的验证过程中仅需要根据加密算法对JWT进行验签解密。相对来说,不会有扩展和性能方面的困扰。

JWT工作流程

通常情况下JWT通过访问一个用于登录的URL来获取。登录成功后,客户端将会在之后的请求的每个请求体中,以Authorization: Bearer {JWT}的格式将JWT设置为请求头。服务端将会对JWT内容进行验签解密,并做出相应的响应。

OAuth 2.0

第三方应用授权在互联网服务中被广泛地运用。在这一功能的背后,大多会使用到OAuth这一授权技术。OAuth是一个用于授权的网络标准,目前主流的版本为2.0版.

使用OAuth 2.0这一授权标准,可以让第三方应用程序获取用户在某一网络服务(例如QQ、GitHub)上有限的账户访问权限。比如,使用QQ的第三方授权可以获取用户的QQ昵称头像等信息。实现方式是通过将身份验证这一环委托给承载用户账户的服务器(通常被称作认证服务器,Authorization Server),并授权第三方应用(通常被称作资源服务器,Resource Server)访问用户账户。

OAuth 2.0中涉及四个角色之间的信息交换,分别是: ·

 用户:用户拥有授权/资源服务器上的账户,在流程中授予客户端访问账户的权限。 ·

 授权服务器:负责验证用户身份,向第三方应用颁发令牌。 · 资源服务器:负责保管用户账户。

· 第三方应用(客户端):想要访问用户账户的应用程序。

OAuth 2.0提供四种不同的授权模式以适应各种应用场景: · 授权码模式 · 密码模式 · 简化模式 客户端模式

继续阅读