BS系统的登录鉴权流程演变(高级必备)

BS系统的登录鉴权流程演变

  • 1 基础知识
    • 1.1 Http Cookie
    • 1.2 重定向与前端路由Vue-router
      • 1.2.1 后端重定向
      • 1.2.2 Vue-router
    • 1.3.JWT简介
    • 1.4 Spring-Security
      • 1.4.1 过滤器链[24]
      • 1.4.3 DelegationFilterProxy的实例化和拦截配置
      • 1.4.4 在项目中使用Spring Security
      • 1.4.5 用户认证
  • 2. 登录鉴权方式演变
    • 2.1前后端不分离的登录鉴权流程#
    • 2.2前后端分离后的登录鉴权流程#
      • 2.2.1 单应用系统
      • 2.2.2 多个微服务的系统

1 基础知识

用户登录是使用指定用户名和密码登录到系统,以对用户的私密数据进行访问和操作。在一个有登录鉴权的BS系统中,通常用户访问数据时,后端拦截请求,对用户进行鉴权,以验证用户身份和权限。用户名、密码等身份信息只需要在登录时输入一次,然后通过前后端的配合,在之后的每次访问都不用再输入了,通常的方案是将身份标识存在cookie中。

实际的登录方案通常较为复杂。一方面需要了解系统的整体架构,包括前端的架构,然后按需设计不同的登录方案;二是需要考虑安全漏洞。我接触过几个系统,从简单的系统到复杂的,在这里把它们的登录方案介绍一下。

在介绍具体的登录方案前,先介绍下登录相关的基础知识。

1.1 Http Cookie

Cookie是由Web服务器向Web浏览器发送的一小段字符串,此后的所有浏览器对服务端的访问都会携带这个字符串。Cookie由Netscape发明,它使得保持HTTP请求的状态(Http协议是无状态协议)变得容易,服务端可以向Cookie中存入任意的信息。Cookie最常见用于已登录用户的鉴权,用户不用每次请求访问时都在页面进行登录。Cookie也有其它用途,比如用于存储购物车列表[3]。

Cookie的使用是通过Http头set-cookie和cookie实现的。在接收到Http请求后,服务端可以向浏览器发送一个或多个Set-Cookie应答头。浏览器会自动存储cookie并在此后的浏览器对服务端的请求中携带Cookie请求头[1]。

服务端向浏览器发送的应答头:

HTTP/2.0 200 OK
Content-Type: text/html
Set-Cookie: yummy_cookie=choco
Set-Cookie: tasty_cookie=strawber浏览器自动将cookie保存,在此后每次浏览器向服务的请求都会自动携带该cookie:GET /sample_page.html HTTP/2.0
Host: www.example.org
Cookie: yummy_cookie=choco; tasty_cookie=strawberry

服务端向浏览器发送应答头的java代码实现如Demo1。你可以为cookie设置存活时间,指定Cookie的域名和路径。Cookie的默认存活时间为浏览器会话结束;设置存活时间后,会话结束不会影响cookie的存活;浏览器会话结束的场景比如关闭浏览器窗口。Cookie的默认所属路径是“/项目路径/相对路径”的上一层路径;当请求路径为Cookie所属的路径及其子路径时,才会携带cookie。还可以为Cookie设置Same-Site、HttpOnly等属性[2],它们与Cookie使用的安全性有关。

public class CookieTestServlet extends HttpServlet {protected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {// 创建cookie对象Cookie yummy = new Cookie("yummy_cookie", "choco");Cookie tasty = new Cookie("tasty_cookie", "strawberry");//默认存活时间为浏览器会话结束;设置存活时间后,会话结束不会影响cookie的存活;浏览器会话结束的场景比如关闭浏览器窗口tasty.setMaxAge(3600);//默认路径是“/项目路径/相对路径”的上一层路径;当请求路径为设置的路径及其子路径时,才会携带cookie;tasty.setPath("/test");//不设置Domain时,Domain的默认值为当前请求的域名(比如localhost、www.example.org等)//将cookie返回给浏览器,通过应答头set-cookieresponse.addCookie(yummy);response.addCookie(tasty);}
}

Demo1 创建Cookie并设置其常用属性的java代码实现

在服务端设置了Cookie的存活时间和路径后,服务端向浏览器发送的应答头如下:

HTTP/2.0 200 OK
Content-Type: text/html
Set-Cookie:tasty_cookie=strawberry; Max-Age=3600; Expires=Thu, 21-Sep-2023 08:33:24 GMT; Path=/test
Set-Cookie:yummy_cookie=choco

1.2 重定向与前端路由Vue-router

1.2.1 后端重定向

早期的系统是前后端不分离的。一个典型的系统使用SSM+JSP的架构。未登录的用户访问系统页面时,会通过后端重定向到登录页,如Demo2。登录时通常将用户名、密码通过form表单提交到后台,后台登录校验不通过时,也会重定向到登录页。

//权限过滤器
public class PermissionFilter implements Filter {public void doFilter(ServletRequest _request, ServletResponse _response,FilterChain chain) throws IOException, ServletException {//如果未登录,重定向到登录页if (!checkLogin(request, response)){response.sendRedirect(request.getContextPath() + "/login.jsp")}chain.doFilter(request, response);}
}

Demo2 权限过滤器的简单实现

1.2.2 Vue-router

在系统的前后端分离后,一个比较常见Web系统,前端使用Vue+Nodejs,后端使用SpringBoot+Spring+Mybatis。在用户认证鉴权业务中,前端应用可独立地提供页面的访问和实现页面间的跳转,后端实现用户认证鉴权的逻辑并提供接口。用户未登录访问系统页面时,页面跳转通常是通过Vue-router实现的。如Demo3,在router的全局前置守卫(router.berforeEach)中,判断用户是否已登录,如果未登录,则跳转(通过路由导航)到登录页。

router.beforeEach((to, from, next) => {//判断是否已登录if (getToken()) {next()  //进入管道中的下一个钩子}else{next(`/login`) //跳转到登录页}
}

Demo3 使用Vue-router.beforeEach进行用户是否登录的判断

对于大多数单页应用,Vue都推荐使用官方的Vue-router。这是由于前端应用的业务功能越来越复杂,单页应用(SPA)成为前端应用的主流形式。Vue-router通过管理URL,实现URL和组件的对应,以及通过URL进行组件之间的切换。可以参考相关博客中的案例,进行Vue-router的安装和简单使用[5]。使用Vue-router,通过改变URL,在不重新请求页面的情况下,就可以更新页面视图。“更新视图但不重新请求页面”是前端路由原理的核心之一,目前在浏览器环境中这一功能的实现主要有两种方式[6]:

利用URL中的hash(“#”)
利用History interface在 HTML5中新增的方法。
如Demo4,在vue-router中是通过mode这一参数控制路由的实现模式的,mode值为“hash“表示第一种方式,值为”history“表示第二种方式。可以使用 router.beforeEach 注册一个全局前置守卫。当一个路由导航触发时,全局前置守卫按照创建顺序调用。每个守卫方法接收三个参数[16]:

  • to: Route: 即将要进入的目标 路由对象
  • from: Route: 当前导航正要离开的路由
  • next: Function: 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。

1.next(): 进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是 confirmed (确认的)。
2.next(false): 中断当前的导航。如果浏览器的 URL 改变了 (可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from 路由对应的地址。
3.next(‘/’) 或者 next({ path: ‘/’ }): 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。你可以向 next 传递任意位置对象,且允许设置诸如 replace: true、name: ‘home’ 之类的选项以及任何用在 router-link 的 to prop 或 router.push 中的选项。
4.next(error): (2.4.0+) 如果传入 next 的参数是一个 Error 实例,则导航会被终止且该错误会被传递给 router.onError() 注册过的回调。
确保要调用 next 方法,否则钩子就不会被 resolved。

export default new Router({// mode: 'hash',mode: 'history',scrollBehavior: () => ({ y: 0 }),routes: constantRouterMap
})

Demo4 创建Vue-router时指定mode为history

1.3.JWT简介

JSON Web Token (JWT) 是一个开放的标准(RFC 7519[8])。它定义了严谨且独立的方式,在多方服务间以JSON对象的格式安全地传递信息。数字签名使传递的信息可验证可信任。JWT签名方式有使用一个secret的HMAC算法和使用公钥/私钥的RSA或ECDSA算法等。

JWT包含head、payload和signature三个部分。比如signature的签名方式使用的是公钥/私钥的RSA算法。payload中一般包含用户名和权限等信息,可以直接从token中获取这些信息。token还有一些其它特性,signature使用公钥解密后的值与head和playload的值做对比是否一致,可以判断token是否被篡改。从系统层面讲,只有对token的签名方拥有私钥。

JWT是基于token鉴权标准之一,常用的标准还有OAuth[9]。token是身份验证过程中用到的令牌,他是验证用户身份和资源权限的临时密钥。一个有效的token允许用户对在线的服务和web应用进行访问直至token过期。这提供了便利,用户不用每次都重新进行登录认证,就可以继续访问资源。这与cookie中的sessionId有类似之处。

JWT的构成#
JSON Web Token(JWT)包含Header、Payload和Signature3个部分,3个部分以点号(.)隔开,他的格式可以表示为xxxxx.yyyyy.zzzzz。

1.4 Spring-Security

Spring Security框架提供了身份认证、权限控制和安全漏洞防护等功能。它在保护spring应用方面是实际上的标准,为保护命令式和反应式应用程序提供一流的支持。

1.4.1 过滤器链[24]

Spring Security的servlet实现是基于servlet过滤器的。如图1中的图①,当客户端向应用发送请求后,servlet容器依据请求的URL创建FilterChain,FilterChain中包含Filter实例和处理HttpServetRequest的Servlet。

Spring提供了DelegatingFilterProxy这个过滤器,它将Servlet容器的生命周期和Spring中的ApplicationContext连接起来。Servlet容器允许使用自己的标准注册Filter实例,但无法识别Spring中定义的beans。你可以通过Servlet容器的机制注册DelegatingFilterProxy并将所有的工作委托给实现Filter接口的Spring Bean。图1中的②展示了DelegatingFilterProxy与FilterChain和Spring的Filter实例的关系。DelegatingFilterProxy从ApplicationContext中搜寻Bean Filter0并调用该Bean Filter0,DelegatingFilterProxy的伪代码实现如Demo5。

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
Filter delegate = getFilterBean(someBeanName);
delegate.doFilter(request, response);
}
Demo5 DelegatingFilterProxy将工作委托给Spring Beans的伪代码

Spring Security对Servlet的支持都包含在FilterChainProxy中。FilterChainProxy是一个由Spring Security提供的特殊的Filter,它允许通过SecurityFilterChain将工作委托给许多Filter实例。FilterChainProxy是一个Bean,通常包装在DelegatingFilterProxy中。图1中的③展示了FilterChainProxy的角色。

请添加图片描述

图1 过滤器链的结构:①Servlet容器中的过滤器链; ②Spring中的DelegatingFilterProxy; ③FilterChainProxy; ④SecurityFilterChain; ⑤多SecurityFilterChain。

1.4.3 DelegationFilterProxy的实例化和拦截配置

DelagtingFilterProxy的初始化和拦截配置在容器启动的时候就完成了。

SpringServletContainerInitializer是ServletContainerInitializer的实现类,且使用@HandlesTypes注解。当容器启动后,会调用SpringServletContainerInitializer的onStartup方法,收集WebApplicationInitializer的子类,并循环调用这些子类的onStartup方法。AbstractSecurityWebApplicationInitializer是WebApplicationInitializer的子类,它的onStartup方法被调用,对DelegationFilterProxy进行实例化并配置拦截路径。图2展示了从容器启动到DelegationFilterProxy完成实例化和拦截配置的流程
请添加图片描述

1.4.4 在项目中使用Spring Security

现在的项目大多都是前后端分离的。以开源项目eladmin[26]为例进行说明。该项目前端使用Vue+Nodejs,后端使用SpringBoot+Spring+Mybatis,基于Spring Security进行用户认证、鉴权授权。项目中引入Spring Security后,进行了用户认证、鉴权授权的功能开发。功能开发主要分为两块内容。

1.4.5 用户认证

用户在登录时进行用户认证。如图3,用户登录时,前端发送登录请求到后端的登录接口。登录接口的逻辑实现如Demo8,其中调用了Spring Security中的方法。实际用户认证逻辑是在Spring Security框架中实现。Spring Security会调用接口UserDetailService的loadUserByUsername方法获取系统中的用户,需要在系统中添加接口UserDetailService的实现类UserDetailServiceImpl,如Demo9。在获取系统的用户后,会调用Spring Security中的DaoAuthenticationProvider的additionalAuthenticationChecks方法,该方法对比登录密码与系统中的密码是否一致,如果一致则用户认证成功,否则认证失败。需要开发者实现的是后台登录接口和获取用户信息的实现类UserDetailServiceImpl,用户认证的逻辑是由Spring Security框架实现的。用户登录接口的url为“/login”,该接口在过滤器配置中配置了所有用户(包括未登录用户)都可以访问。
请添加图片描述

@AnonymousPostMapping(value = "/login")
public ResponseEntity<Object> login(@Validated @RequestBody AuthUserDto authUser, HttpServletRequest request) throws Exception {// 密码解密String password = RsaUtils.decryptByPrivateKey(RsaProperties.privateKey, authUser.getPassword());UsernamePasswordAuthenticationToken authenticationToken =new UsernamePasswordAuthenticationToken(authUser.getUsername(), password);//调用spring-security框架中的方法,进行用户认证Authentication authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken);// 生成令牌String token = tokenProvider.createToken(authentication);// 将令牌token存入redis中// 将令牌token应答到前端
}

Demo8 用户登录接口代码实现

@Service("userDetailsService")
public class UserDetailsServiceImpl implements UserDetailsService {@Overridepublic JwtUserDto loadUserByUsername(String username) {user = userService.findByName(username);jwtUserDto = new JwtUserDto(user,dataService.getDeptIds(user),roleService.mapToGrantedAuthorities(user));return jwtUserDto;}
}

Demo9 系统中添加了UserDetailsService的实现类UserDetailsServiceImpl

2. 登录鉴权方式演变

登录鉴权方式是随着前后端架构的变化而变化的。早期的系统是前后端不分离的。通常前端是freemaker/velocity/jsp+html。后端是SSH或SSM。

后来Vue等前端框架的兴起,使得前后端得以分离。前端是Vue+nodejs,后端是SSM或SpirngBoot。SpringBoot大大简化了应用的配置。

再后来微服务SpringCloud兴起,它包含网关、配置中心、注册中心等组件。多个微服务的登录鉴权实现和单应用系统又略有差异。

2.1前后端不分离的登录鉴权流程#

早期的系统是前后端不分离的。一个典型的系统使用SSM+JSP的架构,技术栈为SpringMVC + Spring + Mybatis + JSP+ Apache + Weblogic。系统的架构图如图5所示。系统的登录鉴权流程如图6所示。系统未登录时,访问系统的请求将被用户权限过滤器拦截,首次进入权限过滤器会生成会话Session,然后通过后端重定向跳转到登录页。在登录页中,用户填写好用户名密码后,提交form表单,请求后台登录接口,后台进行登录校验,登录成功后将用户名、密码等用户信息存入Session中。在后面的每次访问后端接口时,根据携带cookie的jessionId就可以从Session中获取用户信息,如果用户名不为空,就说明已认证过,然后可正常访问后端接口,不用每次访问都进行用户认证。用户登录认证失败后,会跳转到错误页。
请添加图片描述图5 一个前后端不分离的典型系统的架构图请添加图片描述

2.2前后端分离后的登录鉴权流程#

2.2.1 单应用系统

在系统的前后端分离后,一个比较常见Web系统,前端使用Vue+Nodejs,后端使用SpringBoot+Spring+Mybatis。以开源项目eladmin[26]为例进行说明。系统的架构图如图7所示。系统的用户认证流程如图8所示,在用户认证流程中,前端应用可独立地提供页面的访问和实现页面间的跳转,后端实现用户认证的逻辑并提供接口。访问系统的请求通过Vue-Router导航到相应页面,路由导航会触发全局前置守卫的调用。在全局守卫的逻辑中,如果用户未登录,路由会导航到登录页。用户填好用户名和密码进行登录,向后台发起登录的ajax请求,后台会校验用户名密码,校验逻辑是基于SpringSecurity实现的,如果校验通过,则生成JWT类型的token,应答到浏览器。浏览器端会保存token到cookie中,并创建后端接口请求拦截器,拦截请求并将cookie中的token放到请求头Authentication中。请求到达后端服务后,后端的用户权限过滤器会判断Authentication中的token是否已登录过(判断在redis中是否存在),并基于SpringSecurity进行鉴权,如果鉴权通过,则正常访问接口。不用每次访问后端接口都进行用户认证。如果登录失败,则应答错误状态码和错误信息,浏览器会在页面进行错误提示。
请添加图片描述
请添加图片描述

2.2.2 多个微服务的系统

后来微服务逐渐兴起,SpringCloud是热门的技术之一。SpringCloud包含一系列组件,包括Eureka、Ribbon、Zuul、Feign和Config Server等,方便进行微服务的管理、调用和配置。一个比较常见的Web系统,前端使用Vue+Nodejs,后端使用SpringBoot+SpringCloud+Spring+Mybatis。系统的架构图如图9所示。系统的用户认证流程如图10所示。与单应用系统的用户认证流程相比,主要有2点不同。一是用户认证逻辑会放在独立的鉴权微服务中。二是不是每个包含业务接口的微服务都放一个用户权限过滤器,而将过滤器放在网关微服务中。如图10的架构图,每个后端请求都会经过网关,在网关中放入用户权限过滤器是合适的。在实际业务中,网关作为后端微服务的唯一入口,后端微服务则放在内网中,不能不通过网关直接访问后端微服务的接口。用户认证成功后,后端会应答set-cookie:token=aaaaa.bbbb.ccccc到浏览器,浏览器将token存入cookie中,后续的接口访问请求都会携带该cookie,请求经过权限过滤器时,过滤器将cookie中的token取出,判断该token是否已登录过(判断在redis中是否存在),并基于SpringSecurity进行鉴权,如果鉴权通过,则正常访问接口,不用每次都进行用户认证。不过,token放在Cookie中是不建议的,建议放在请求头Authrization中,详细可参考1.4.3节。
请添加图片描述
请添加图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring Cloud - Openfeign 实现原理分析

OpenFeign简介 OpenFeign 是一个声明式 RESTful 网络请求客户端。OpenFeign 会根据带有注解的函数信息构建出网络请求的模板,在发送网络请求之前,OpenFeign 会将函数的参数值设置到这些请求模板中。虽然 OpenFeign 只能支持基于文本的网络请求,但是它可以极大简化网络请求的…

六西格玛绿带培训:量化进步的关键

在追求卓越的道路上&#xff0c;六西格玛绿带培训成为了一种革命性的思维方式&#xff0c;让我们能够以科学和系统的方法提升过程性能。但在这个追求中&#xff0c;我们如何确定我们的进步&#xff1f;过程能力分析为我们提供了明确的答案。通过计算过程能力&#xff0c;即6σ&…

竞赛 python 爬虫与协同过滤的新闻推荐系统

1 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; python 爬虫与协同过滤的新闻推荐系统 &#x1f947;学长这里给一个题目综合评分(每项满分5分) 难度系数&#xff1a;3分工作量&#xff1a;3分创新点&#xff1a;4分 该项目较为新颖&…

python关于字符串基础学习

字符串 python字符串是不可改变的 Python不支持单字符类型&#xff0c;单字符也是作为一个字符串使用的。 字符串编码 python3直接支持Unicode,可以表示世界上任何书面语言的字符 python3的字符默认就是16位Unicode编码&#xff0c;ASCII是Unicode的子集 使用内置函数 ord()…

二十六 超级数据查看器 讲解稿 用输入值批量更新字段

二十六 超级数据查看器 讲解稿 用输入值批量更新字段 ​点击此处 以新页面 打开B站 播放当前教学视频 点击访问app下载页面 百度手机助手 下载地址 ​ 大家好&#xff0c;今天我们讲一下超级数据查看器的输入更新功能&#xff0c;输入更新功能是将选择的TXT文档的数据&…

【竞技宝】DOTA2-PGL联赛:niu神无解 LGD2-0轻松击败DH

北京时间2024年3月26日,PGL联赛中国区的比赛在昨日正式打响,首日共进行了四场胜者组首轮的比赛,第四场比赛由LGD对阵DH。本场比赛,DH两局都在前中期和LGD有来有回,但niu的中期节奏完全摧毁了DH,最终LGD2-0轻松击败DH。以下是本场比赛的详细战报。 第一局: 首局比赛,LGD在天辉方…

使用Docker Compose一键部署前后端分离项目(图文保姆级教程)

一、安装Docker和docker Compose 1.Docker安装 //下载containerd.io包 yum install https://download.docker.com/linux/fedora/30/x86_64/stable/Packages/containerd.io-1.2.6-3.3.fc30.x86_64.rpm //安装依赖项 yum install -y yum-utils device-mapper-persistent-data l…

基于springboot实现课程作业管理系统项目【项目源码+论文说明】计算机毕业设计

基于springboot实现课程作业管理系统演示 摘要 随着科学技术的飞速发展&#xff0c;社会的方方面面、各行各业都在努力与现代的先进技术接轨&#xff0c;通过科技手段来提高自身的优势&#xff0c;课程作业管理系统当然也不能排除在外。课程作业管理系统是以实际运用为开发背景…

OpenCV4.9关于矩阵上的掩码操作

返回&#xff1a;OpenCV系列文章目录&#xff08;持续更新中......&#xff09; 上一篇:如何使用OpenCV扫描图像、查找表和时间测量 下一篇:OpenCV4.9的是如何进行图像操作 引言&#xff1a; 矩阵上的掩码操作非常简单。这个想法是&#xff0c;我们根据掩码矩阵&#xff08…

【机器学习之---数学】随机游走

every blog every motto: You can do more than you think. https://blog.csdn.net/weixin_39190382?typeblog 0. 前言 随机游走 1. 概念 1.1 例1 在你的饮食俱乐部度过了一个富有成效的晚上后&#xff0c;你在不太清醒的状态下离开了。因此&#xff0c;你会醉醺醺地在展…

Day21|二叉树part07:530.二叉搜索树的最小绝对差、501.二叉搜索树中的众数、236. 二叉树的最近公共祖先

530. *二叉搜索树的最小绝对差&#xff08;双指针题型&#xff09; 众所周知二叉搜索树的中序遍历序列是一个有序数组&#xff0c;因此最基本的方法就是遍历得到中序序列再进行计算&#xff0c;实际上可以用双指针法&#xff0c;记录中序遍历前一个指针和当前指针的差值&#…

GPT-SoVITS教程,接入酒馆AI,SillyTavern-1.11.5,让AI女友声若幽兰

本次分享一下如何将GPT-SoVITS接入SillyTavern-1.11.5项目&#xff0c;让让AI女友声若幽兰&#xff0c;首先明确一下&#xff0c;SillyTavern-1.11.5只是一个前端项目&#xff0c;它没有任何大模型文本生成能力&#xff0c;所以后端必须有一个api服务来流式生成对话文本&#x…