当前位置: 首页 > news >正文

网站设计 模板专业网站建设怎么样

网站设计 模板,专业网站建设怎么样,营销型网站的运营配套不包括,wordpress什么都不做很慢在Web系统中#xff0c;如果没有登录功能和登录认证#xff0c;是可以直接访问到Web系统的后台的。 这是不安全的#xff0c;所以我们今天的主题就是登录认证。最终要实现的效果是#xff1a; 如果用户名密码错误#xff0c;不允许登录系统。如果用户名和密码都正确…在Web系统中如果没有登录功能和登录认证是可以直接访问到Web系统的后台的。 这是不安全的所以我们今天的主题就是登录认证。最终要实现的效果是 如果用户名密码错误不允许登录系统。如果用户名和密码都正确则登录成功可以访问系统。 登录功能 需求 在登录界面中输入用户的用户名和密码然后点击 登录 按钮请求服务器服务端判断用户输入的用户名或者密码是否正确。如果正确则返回登录成功的结果前端跳转至系统首页。 接口描述 我们参照接口文档中的 其他接口 - 登录接口 思路分析 怎么样才算登录成功了呢 用户名和密码都输入正确登录成功 否则登录失败 登录功能的本质是什么 查询 根据用户名和密码查询员工信息 功能开发 1). 准备实体类 LoginInfo 封装登录成功后 返回给前端的数据 。 /*** 登录成功结果封装类*/ Data NoArgsConstructor AllArgsConstructor public class LoginInfo {private Integer id; //员工IDprivate String username; //用户名private String name; //姓名private String token; //令牌 }2). 定义LoginController Slf4j RestController public class LoginController {Autowiredprivate EmpService empService;PostMapping(/login)public Result login(RequestBody Emp emp){log.info(员工来登录啦 , {}, emp);LoginInfo loginInfo empService.login(emp);if(loginInfo ! null){return Result.success(loginInfo);}return Result.error(用户名或密码错误~);}} 3). EmpService接口中增加 login 登录方法 /*** 登录*/ LoginInfo login(Emp emp); 4). EmpServiceImpl 实现login方法 Override public LoginInfo login(Emp emp) {Emp empLogin empMapper.getUsernameAndPassword(emp);if(empLogin ! null){LoginInfo loginInfo new LoginInfo(empLogin.getId(), empLogin.getUsername(), empLogin.getName(), null);return loginInfo;}return null; } 5). EmpMapper增加接口方法 /*** 根据用户名和密码查询员工信息*/ Select(select * from emp where username #{username} and password #{password}) Emp getUsernameAndPassword(Emp emp); 现在登录功能就已经开发完毕但是当在浏览器中输入地址web系统的功能接口时发现没有登录仍然可以进入到后端管理系统页面。 而真正的登录功能应该是登陆后才能访问后端系统页面不登陆则跳转到登陆页面进行登陆。 为什么会出现这个问题其实原因很简单就是因为针对于我们当前所开发的部门管理、员工管理以及文件上传等相关接口来说我们在服务器端并没有做任何的判断没有去判断用户是否登录了。所以无论用户是否登录都可以访问部门管理以及员工管理的相关数据。所以我们目前所开发的登录功能它只是徒有其表。而我们要想解决这个问题我们就需要完成一步非常重要的操作登录校验。 那接下来我们就进入到今天课程内容的第二章节登录校验功能的实现。 登录校验 所谓登录校验指的是在服务器端接收到浏览器发送过来的请求之后首先需要对请求进行校验。先要校验一下用户登录了没有如果用户已经登录了就直接执行对应的业务操作就可以了如果用户没有登录此时就不允许他执行相关的业务操作直接给前端响应一个错误的结果最终跳转到登录页面要求他登录成功之后再来访问对应的数据。 思路 了解完什么是登录校验之后接下来我们分析一下登录校验大概的实现思路。 首先我们在宏观上先有一个认知 前面在讲解HTTP协议的时候我们提到HTTP协议是无状态协议。什么又是无状态的协议 所谓无状态指的是每一次请求都是独立的下一次请求并不会携带上一次请求的数据。而浏览器与服务器之间进行交互基于HTTP协议也就意味着现在我们通过浏览器来访问了登陆这个接口实现了登陆的操作接下来我们在执行其他业务操作时服务器也并不知道这个员工到底登陆了没有。因为HTTP协议是无状态的两次请求之间是独立的所以是无法判断这个员工到底登陆了没有。 那应该怎么来实现登录校验呢具体的实现思路可以分为两部分 在员工登录成功后将用户登录成功的信息存起来记录用户已经登录成功的标记。 在浏览器发起请求时需要在服务端进行统一拦截拦截后进行登录校验。 想要判断员工是否已经登录我们需要在员工登录成功之后存储一个登录成功的标记接下来在每一个接口方法执行之前先做一个条件判断判断一下这个员工到底登录了没有。如果是登录了就可以执行正常的业务操作如果没有登录会直接给前端返回一个错误的信息前端拿到这个错误信息之后会自动的跳转到登录页面。 我们程序中所开发的查询功能、删除功能、添加功能、修改功能都需要使用以上套路进行登录校验。此时就会出现相同代码逻辑每个功能都需要编写就会造成代码非常繁琐。 为了简化这块操作我们可以使用一种技术统一拦截技术。 通过统一拦截的技术我们可以来拦截浏览器发送过来的所有的请求拦截到这个请求之后就可以通过请求来获取之前所存入的登录标记在获取到登录标记且标记为登录成功就说明员工已经登录了。如果已经登录我们就直接放行(意思就是可以访问正常的业务接口了)。 要完成以上操作会涉及到web开发中的两个技术 会话技术用户登录成功之后在后续的每一次请求中都可以获取到该标记。 统一拦截技术过滤器Filter、拦截器Interceptor 下面我们先学习会话技术然后再学习统一拦截技术。 会话技术 介绍了登录校验的大概思路之后我们先来学习下会话技术。 介绍 什么是会话 在我们日常生活当中会话指的就是谈话、交谈。 在web开发当中会话指的是浏览器与服务器之间的一次连接我们就称为一次会话。 用户打开浏览器第一次访问服务器的时候这个会话就建立了直到有任何一方断开连接此时会话就结束。在一次会话当中是可以包含多次请求和响应的。 比如打开了浏览器来访问web服务器上的资源浏览器不能关闭、服务器不能断开 第1次访问的是登录的接口完成登录操作 第2次访问的是部门管理接口查询所有部门数据 第3次访问的是员工管理接口查询员工数据 只要浏览器和服务器都没有关闭以上3次请求都属于一次会话当中完成的。 需要注意的是会话是和浏览器关联的当有三个浏览器客户端和服务器建立了连接时就会有三个会话。同一个浏览器在未关闭之前请求了多次服务器这多次请求是属于同一个会话。比如1、2、3这三个请求都是属于同一个会话。当我们关闭浏览器之后这次会话就结束了。而如果我们是直接把web服务器关了那么所有的会话就都结束了。 会话跟踪一种维护浏览器状态的方法服务器需要识别出多次请求是否来自于同一浏览器以便在同一个会话的多次请求间共享数据。 服务器会接收很多的请求但是服务器是需要识别出这些请求是不是同一个浏览器发出来的。比如1和2这两个请求是不是同一个浏览器发出来的3和5这两个请求不是同一个浏览器发出来的。如果是同一个浏览器发出来的就说明是同一个会话。如果是不同的浏览器发出来的就说明是不同的会话。而识别多次请求是否来自于同一浏览器的过程我们就称为会话跟踪。 使用会话跟踪技术就是要完成在同一个会话中多个请求之间进行共享数据。 为什么要共享数据呢 由于HTTP是无状态协议在后面请求中怎么拿到前一次请求生成的数据呢此时就需要在一次会话的多次请求之间进行数据共享 会话跟踪技术有两种 Cookie客户端会话跟踪技术数据存储在客户端浏览器当中 Session服务端会话跟踪技术数据存储在储在服务端 令牌技术 会话跟踪方案 上面我们介绍了什么是会话什么是会话跟踪并且也提到了会话跟踪 3 种常见的技术方案。接下来我们就来对比一下这 3 种会话跟踪的技术方案来看一下具体的实现思路以及它们之间的优缺点。 方案一Cookie cookie 是客户端会话跟踪技术它是存储在客户端浏览器中的我们使用 cookie 来跟踪会话我们就可以在浏览器第一次发起请求来请求服务器的时候我们在服务器端来设置一个cookie。 比如 第一次请求了登录接口如果登录成功我们就可以在服务器端设置一个cookie在 cookie 中就可以将多次请求间需要共享的数据存储在cookie中。比如存储登录成功的标识用户相关的一些数据信息我可以在 cookie 当中来存储当前登录用户的用户名用户的ID。之后服务器在给客户端响应数据的时候会自动将 cookie 响应给浏览器浏览器接收到响应回来的 cookie 之后会自动的将 cookie 的值存储在浏览器本地。接下来在后续的每一次请求当中都会将浏览器本地所存储的 cookie 自动地携带到服务端。 接下来在服务端就可以获取到 cookie 的值。我们可以去判断一下这个 cookie 的值是否存在如果不存在这个cookie就说明客户端之前是没有访问登录接口的如果存在 cookie 的值就说明客户端之前已经登录完成了。这样我们就可以基于 cookie 在同一次会话的不同请求之间来共享数据。 我刚才在介绍流程的时候用了 3 个自动 服务器会 自动 将 cookie 响应给浏览器。 浏览器接收到响应回来的数据之后会 自动 将 cookie 存储在浏览器本地。 在后续的请求当中浏览器会 自动 将 cookie 携带到服务器端。 为什么这一切都是自动化进行的 因为 cookie 是 HTTP 协议中支持的技术而各大浏览器厂商都支持了这一标准。在 HTTP 协议官方提供了下面的响应头和请求头 响应头 Set-Cookie 当在服务器端设置Cookie后服务器会自动在响应头Set-Cookie中将cookie响应给客户端浏览器而客户端浏览器在响应头中见到Set-Cookie时会自动将cookie保存在浏览器本地 请求头 Cookie在后续的每一次请求中都会将当前服务器所涉及到的cookie全部携带到服务器端通过请求头Cookie携带 代码测试 Slf4j RestController public class SessionController {//设置Cookie服务器给客户端响应cookieGetMapping(/c1)public Result cookie1(HttpServletResponse response){//设置Cookie/响应Cookieresponse.addCookie(new Cookie(login_username,itheima)); return Result.success();}//获取Cookie在服务端获取请求中携带过来的cookieGetMapping(/c2)public Result cookie2(HttpServletRequest request){//获取到传递过来的所有cookieCookie[] cookies request.getCookies();//循环遍历所有cookiefor (Cookie cookie : cookies) {if(cookie.getName().equals(login_username)){System.out.println(login_username: cookie.getValue()); //输出name为login_username的cookie}}return Result.success();} } 测试  A. 访问c1接口设置Cookiehttp://localhost:8080/c1 我们可以看到在服务器端设置的cookie通过响应头Set-Cookie响应给浏览器并且浏览器会将Cookie存储在浏览器本地。 B. 访问c2接口 http://localhost:8080/c2此时浏览器会自动的将Cookie携带到服务端通过请求头Cookie携带到服务器。 优缺点 优点HTTP协议中支持的技术像Set-Cookie 响应头的解析以及 Cookie 请求头数据的携带都是浏览器自动进行的是无需我们手动操作的 缺点 移动端APP(Android、IOS)中无法使用Cookie 不安全用户可以自己禁用Cookie Cookie不能跨域 现在的项目大部分都是前后端分离的前后端最终也会分开部署前端部署在服务器 192.168.150.200 上端口 80后端部署在 192.168.150.100上端口 8080 我们打开浏览器直接访问前端工程访问urlhttp://192.168.150.200/login.html 然后在该页面发起请求到服务端而服务端所在地址不再是localhost而是服务器的IP地址192.168.150.100假设访问接口地址为http://192.168.150.100:8080/login 那此时就存在跨域操作了因为我们是在 http://192.168.150.200/login.html 这个页面上访问了http://192.168.150.100:8080/login 接口 此时如果服务器设置了一个Cookie这个Cookie是不能使用的因为Cookie无法跨域 区分跨域的维度三个维度有任何一个维度不同那就是跨域操作 协议 IP/协议 端口 举例 http://192.168.150.200/login.html ---------- https://192.168.150.200/login [协议不同跨域] http://192.168.150.200/login.html ---------- http://192.168.150.100/login [IP不同跨域] http://192.168.150.200/login.html ---------- http://192.168.150.200:8080/login [端口不同跨域] http://192.168.150.200/login.html ---------- http://192.168.150.200/login [不跨域] 方案二Session 它是服务器端会话跟踪技术所以它是存储在服务器端的。Session 的底层是基于 Cookie 来实现的在Cookie中存储的是Session对象的IDSet-CookieCookie 获取Session 浏览器在第一次请求服务器的时候可以直接在服务器中获取到会话对象Session。如果是第一次请求Session 会话对象是不存在的这个时候服务器会自动的创建一个会话对象Session 。而每一个会话对象Session 它都有一个ID示意图中Session后面括号中的1就表示ID我们称之为 Session 的ID。 响应Cookie (JSESSIONID) 接下来服务器端在给浏览器响应数据的时候它会将 Session 的 ID 通过 Cookie 响应给浏览器。就是在响应头中增加了一个 Set-Cookie 响应头。 cookie 的名是固定是 JSESSIONID 代表服务器端会话对象 Session 的 IDcookie的值就是Session对象的ID。浏览器会自动识别这个响应头然后自动将Cookie存储在浏览器本地。 查找Session 接下来在后续的每一次请求当中都会将 Cookie 的数据获取出来并且携带到服务端。接下来服务器拿到JSESSIONID这个 Cookie 的值也就是 Session 的ID。拿到 ID 之后就会从众多的 Session 当中来找到当前请求对应的会话对象Session。 这样我们是不是就可以通过 Session 会话对象在同一次会话的多次请求之间来共享数据了好这就是基于 Session 进行会话跟踪的流程。 代码测试 Slf4j RestController public class SessionController {GetMapping(/s1)public Result session1(HttpSession session){log.info(HttpSession-s1: {}, session.hashCode());session.setAttribute(loginUser, tom); //往session会话对象中存储数据return Result.success();}GetMapping(/s2)public Result session2(HttpServletRequest request){HttpSession session request.getSession();log.info(HttpSession-s2: {}, session.hashCode());Object loginUser session.getAttribute(loginUser); //从session会话对象中获取数据log.info(loginUser: {}, loginUser);return Result.success(loginUser);} } 测试 A. 访问 s1 接口http://localhost:8080/s1 请求完成之后在响应头中就会看到有一个Set-Cookie的响应头里面响应回来了一个Cookie就是JSESSIONID这个就是服务端会话对象 Session 的ID。 B. 访问 s2 接口http://localhost:8080/s2 接下来在后续的每次请求时都会将Cookie的值携带到服务端那服务端呢接收到Cookie之后会自动的根据JSESSIONID的值找到对应的会话对象Session。 那经过这两步测试大家也会看到在控制台中输出如下日志 两次请求获取到的Session会话对象的hashcode是一样的就说明是同一个会话对象。而且第一次请求时往Session会话对象中存储的值在第二次请求时也获取到了。 那这样我们就可以通过Session会话对象在同一个会话的多次请求之间数据共享了。 优缺点 优点Session是存储在服务端的安全 缺点 服务器集群环境下无法直接使用Session 移动端APP(Android、IOS)中无法使用Cookie 用户可以自己禁用Cookie Cookie不能跨域 PSSession 底层是基于Cookie实现的会话跟踪如果Cookie不可用则该方案也就失效了。 服务器集群环境为何无法使用Session 首先第一点我们现在所开发的项目一般都不会只部署在一台服务器上因为一台服务器会存在一个很大的问题就是单点故障。所谓单点故障指的就是一旦这台服务器挂了整个应用都没法访问了。 所以在现在的企业项目开发当中最终部署的时候都是以集群的形式来进行部署也就是同一个项目它会部署在多台服务器上。比如这个项目我们现在就部署了 3 份。 而用户在访问的时候到底访问这三台其中的哪一台其实用户在访问的时候他会访问一台前置的服务器我们叫负载均衡服务器我们在后面项目当中会详细讲解。目前大家先有一个印象负载均衡服务器它的作用就是将前端发起的请求均匀的分发给后面的这三台服务器。 此时假如我们通过 session 来进行会话跟踪可能就会存在这样一个问题。用户打开浏览器要进行登录操作此时会发起登录请求。登录请求到达负载均衡服务器将这个请求转给了第一台 Tomcat 服务器。 Tomcat 服务器接收到请求之后要获取到会话对象session。获取到会话对象 session 之后要给浏览器响应数据最终在给浏览器响应数据的时候就会携带这么一个 cookie 的名字就是 JSESSIONID 下一次再请求的时候是不是又会将 Cookie 携带到服务端 好。此时假如又执行了一次查询操作要查询部门的数据。这次请求到达负载均衡服务器之后负载均衡服务器将这次请求转给了第二台 Tomcat 服务器此时他就要到第二台 Tomcat 服务器当中。根据JSESSIONID 也就是对应的 session 的 ID 值要找对应的 session 会话对象。 我想请问在第二台服务器当中有没有这个ID的会话对象 Session 是没有的。此时是不是就出现问题了我同一个浏览器发起了 2 次请求结果获取到的不是同一个会话对象这就是Session这种会话跟踪方案它的缺点在服务器集群环境下无法直接使用Session。 大家会看到上面这两种传统的会话技术在现在的企业开发当中是不是会存在很多的问题。 为了解决这些问题在现在的企业开发当中基本上都会采用第三种方案通过令牌技术来进行会话跟踪。接下来我们就来介绍一下令牌技术来看一下令牌技术又是如何跟踪会话的。 方案三 - 令牌技术 令牌其实它就是一个用户身份标识其本质就是一个字符串。 如果通过令牌技术来跟踪会话以登录功能为例当浏览器端发起请求访问登录接口时如果前端传递的用户名和密码都是正确的此时成功登录登录成功后在服务器端就会创建一个令牌在响应数据的时候一并将令牌响应给前端。 接下来在前端程序中接收到令牌后需要将这个令牌存储起来。这个存储可以存储在 cookie 当中也可以存储在其他的存储空间(比如localStorage)当中。 在后续的每一次请求中都需要将令牌携带到服务端。携带到服务端之后就需要校验令牌的有效性。如果令牌是有效的就说明用户已经执行了登录操作如果令牌是无效的就说明用户之前并未执行登录操作。 此时如果是在同一次会话的多次请求之间想共享数据就可以将共享的数据存储在令牌中。 优缺点 优点 支持PC端、移动端 解决集群环境下的认证问题因为令牌这种方案不需要在服务器端保存任何信息 减轻服务器的存储压力无需在服务器端存储 缺点需要自己实现包括令牌的生成、令牌的传递、令牌的校验 JWT令牌最典型的应用场景就是登录认证 在浏览器发起请求来执行登录操作此时会访问登录的接口如果登录成功之后我们需要生成一个jwt令牌将生成的 jwt令牌返回给前端。 前端拿到jwt令牌之后会将jwt令牌存储起来。在后续的每一次请求中都会将jwt令牌携带到服务端。 服务端统一拦截请求之后先来判断一下这次请求有没有把令牌带过来如果没有带过来直接拒绝访问如果带过来了还要校验一下令牌是否是有效。如果有效就直接放行进行请求的处理。 在JWT登录认证的场景中我们发现整个流程当中涉及到两步操作 在登录成功之后要生成令牌。 每一次请求当中要接收令牌并对令牌进行校验。 JWT令牌 前面我们介绍了基于令牌技术来实现会话追踪。这里所提到的令牌就是用户身份的标识其本质就是一个字符串。令牌的形式有很多我们使用的是功能强大的 JWT令牌。 介绍 JWT全称 JSON Web Token 官网https://jwt.io/定义了一种简洁的、自包含的格式用于在通信双方以json数据的格式安全的传输信息。由于数字签名的存在这些信息是可靠的。 简洁是指jwt就是一个简单的字符串。可以在请求参数或者是请求头当中直接传递。 自包含指的是jwt令牌看似是一个随机的字符串但是我们是可以根据自身的需求在jwt令牌中存储自定义的数据内容。如可以直接在jwt令牌中存储用户的相关信息。 简单来讲jwt就是将原始的json数据格式进行了安全的封装这样就可以直接基于jwt在通信双方安全的进行信息传输了。 JWT的组成 JWT令牌由三个部分组成三个部分之间使用英文的点来分割 第一部分Header(头 记录令牌的类型、令牌的签名算法等。 例如{alg:HS256,type:JWT} 第二部分Payload(有效载荷携带一些自定义信息、默认信息等。 例如{id:1,username:Tom} 第三部分Signature(签名防止Token被篡改、确保安全性。基于一定的加密算法计算出来的在进行加密操作时会将header和payload融入进去并且还要加入我们指定的密钥,最终基于一定的签名算法生成jwt令牌的签名部分这个签名算法就是在创建令牌时要指定的签名算法。 签名的目的就是为了防jwt令牌被篡改而正是因为jwt令牌最后一个部分数字签名的存在所以整个jwt 令牌是非常安全可靠的。一旦jwt令牌当中任何一个部分、任何一个字符被篡改了整个令牌在校验的时候都会失败所以它是非常安全可靠的。 JWT是如何将原始的JSON格式数据转变为字符串的呢 在生成JWT令牌时会对JSON格式的数据进行base64编码 Base64是一种基于64个可打印的字符来表示二进制数据的编码方式。既然能编码那也就意味着也能解码。所使用的64个字符分别是A到Z、a到z、 0- 9一个加号一个斜杠加起来就是64个字符。任何数据经过base64编码之后最终就会通过这64个字符来表示。当然还有一个符号那就是等号。等号它是一个补位的符号 需要注意的是Base64是编码方式而不是加密方式。 生成和校验jwt令牌 1). 首先我们先来实现JWT令牌的生成。要想使用JWT令牌需要先引入JWT的依赖 !-- JWT依赖-- dependencygroupIdio.jsonwebtoken/groupIdartifactIdjjwt/artifactIdversion0.9.1/version /dependency 在引入完JWT来赖后就可以调用工具包中提供的API来完成JWT令牌的生成和校验。工具类Jwts 2). 生成JWT代码实现 /*** 生成JWT令牌* 1、使用Jwts工具类的builder()方法来构建Jwt令牌* 2、同时链式调用 signWith(签名算法,密钥)方法 指定生成签名时使用的签名算法* 3、再继续链式调用 addClaims(Map集合)方法 给令牌中添加自定义数据* 4、对于生成的jwt令牌还可以链式调用 setExpiration(Date对象)方法 指定有效期* 5、最后调用 compact()方法 生成jwt令牌*/ Test public void testGenerateJwt() {//将需要加入令牌中的自定义信息封装在MapString,Object集合中MapString, Object claims new HashMap();claims.put(id, 10);claims.put(username, itheima);String jwt Jwts.builder().signWith(SignatureAlgorithm.HS256, aXRjYXN0).addClaims(claims).setExpiration(new Date(System.currentTimeMillis() 12 * 3600 * 1000)).compact();System.out.println(jwt); } 签名算法可以看Jwt官网的下图部分得知 密钥部分可以传一个基于base64编码的字符串进去 通过浏览器搜索Base64在线编解码获得 运行测试方法 eyJhbGciOiJIUzI1NiJ9.eyJpZCI6MSwiZXhwIjoxNjcyNzI5NzMwfQ.fHi0Ub8npbyt71UqLXDdLyipptLgxBUg_mSuGJtXtBk 输出的结果就是生成的JWT令牌,通过英文的点分割对三个部分进行分割我们可以将生成的令牌复制一下然后打开JWT的官网将生成的令牌直接放在Encoded位置此时就会自动的将令牌解析出来 第一部分解析出来看到JSON格式的原始数据所使用的签名算法为HS256。 第二个部分是我们自定义的数据之前我们自定义的数据就是id还有一个exp代表的是我们所设置的过期时间。 由于前两个部分是base64编码所以是可以直接解码出来。但最后一个部分并不是base64编码是经过签名算法计算出来的所以最后一个部分是不会解析的。 3). 校验JWT令牌(解析生成的令牌) /*** 解析jwt令牌* 1.使用Jwts调用 parser()方法 解析令牌* 2.链式调用setSigningKey(生成令牌时的密钥)方法指定生成令牌时的密钥* 3.继续链式调用 parseClaimsJws()方法 解析传过来的jwt令牌* 4.续集链式调用 getBody()方法 获取Claims它是生成令牌时往令牌中添加的自定义信息*/ Test public void testParseJwt() {Claims claims Jwts.parser().setSigningKey(aXRjYXN0).parseClaimsJws(eyJhbGciOiJIUzI1NiJ9.eyJpZCI6MTAsInVzZXJuYW1lIjoiaXRoZWltYSIsImV4cCI6MTcwMTkwOTAxNX0.N-MD6DmoeIIY5lB5z73UFLN9u7veppx1K5_N_jS9Yko).getBody();System.out.println(claims); } 运行测试方法 {id10, usernameitheima, exp1701909015} 令牌解析后我们可以看到id和过期时间如果在解析的过程当中没有报错就说明解析成功了。 登录时下发令牌 JWT令牌的生成和校验的基本操作我们已经学习完了接下来我们就需要在案例当中通过JWT令牌技术来跟踪会话。具体的思路我们前面已经分析过了主要就是两步操作 生成令牌 登录成功之后生成一个JWT令牌并把这个令牌返回给前端 校验令牌 拦截前端请求从请求中获取到令牌对令牌进行解析校验 首先来完成登录成功之后生成一个JWT令牌并把这个令牌返回给前端。 实现步骤 1.自定义定义JWT令牌操作的工具类 package com.itheima.util;import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm;import java.util.Date; import java.util.Map;public class JwtUtils {private static String signKey SVRIRUlNQQ;private static Long expire 43200000L;/*** 生成JWT令牌* return*/public static String generateJwt(MapString,Object claims){String jwt Jwts.builder().addClaims(claims).signWith(SignatureAlgorithm.HS256, signKey).setExpiration(new Date(System.currentTimeMillis() expire)).compact();return jwt;}/*** 解析JWT令牌* param jwt JWT令牌* return JWT第二部分负载 payload 中存储的内容*/public static Claims parseJWT(String jwt){Claims claims Jwts.parser().setSigningKey(signKey).parseClaimsJws(jwt).getBody();return claims;} }2.完善 EmpServiceImpl中的 login 方法逻辑 登录成功生成JWT令牌并返回 Override public LoginInfo login(Emp emp) {//根据用户名和密码查询用户信息Emp empLogin empMapper.getUsernameAndPassword(emp);//如果用户信息不为nullif(empLogin ! null){//1. 生成JWT令牌MapString,Object dataMap new HashMap();dataMap.put(id, empLogin.getId());dataMap.put(username, empLogin.getUsername());String jwt JwtUtils.generateJwt(dataMap);LoginInfo loginInfo new LoginInfo(empLogin.getId(), empLogin.getUsername(), empLogin.getName(), jwt);return loginInfo;}return null; } 重启服务打开 Apifox 测试登录接口 打开浏览器完成前后端联调操作利用开发者工具抓取一下网络请求 登录请求完成后可以看到JWT令牌已经响应给了前端此时前端就会将JWT令牌存储在浏览器本地。 服务器响应的JWT令牌存储在本地浏览器哪里了呢 在当前案例中JWT令牌存储在浏览器的本地存储空间 localstorage中。 localstorage 是浏览器的本地存储在移动端也是支持的。 我们在发起一个查询部门数据的请求此时我们可以看到在请求头中包含一个token(JWT令牌)在后续的每一次请求中浏览器都会将这个令牌在请求头中携带到服务端。 过滤器Filter 怎么样 统一拦截前端所有的请求来 校验令牌的有效性 这里我们会学习两种解决方案 Filter过滤器 Interceptor拦截器 Filter快速入门 什么是Filter Filter表示过滤器是 JavaWeb三大组件(Servlet、Filter、Listener)之一。 过滤器可以把前端对资源的请求拦截下来过滤器处理完毕之后才可以访问对应的资源 过滤器一般完成一些通用的操作比如登录校验、统一编码处理、敏感字符处理等。 下面我们通过Filter快速入门程序掌握过滤器的基本使用操作 第1步定义过滤器 1.定义一个类实现 Filter 接口并重写其所有方法。 第2步配置过滤器Filter类上加 WebFilter 注解配置拦截资源的路径。引导类上加 ServletComponentScan 开启Servlet组件支持。 1). 定义过滤器 public class DemoFilter implements Filter {//过滤器的初始化方法在web服务器启动的时候会自动的创建Filter过滤器对象//然后自动调用init初始化方法只会被调用一次public void init(FilterConfig filterConfig) throws ServletException {System.out.println(init ...);}//每拦截到一次请求之后就会调用一次doFilter()方法这个方法会被调用多次public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {System.out.println(拦截到了请求...);chain.doFilter(request, response);}//关闭服务器时会自动的调用销毁方法destroy只会被调用一次public void destroy() {System.out.println(destroy ... );} } 2). 配置过滤器 在定义完Filter之后还需要完成Filter的配置在Filter类上添加一个注解WebFilter并指定属性urlPatterns通过这个属性指定过滤器要拦截的请求路径。 WebFilter(urlPatterns /*) //配置过滤器要拦截的请求路径 /* 表示拦截浏览器的所有请求 public class DemoFilter implements Filter {//初始化方法, web服务器启动, 创建Filter实例时调用, 只调用一次public void init(FilterConfig filterConfig) throws ServletException {System.out.println(init ...);}//拦截到请求时,调用该方法,可以调用多次public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {System.out.println(拦截到了请求...);chain.doFilter(request, response);}//销毁方法, web服务器关闭时调用, 只调用一次public void destroy() {System.out.println(destroy ... );} } 在Filter类上添加WebFilter注解之后还需要在启动类上面添加注解ServletComponentScan通过ServletComponentScan注解来开启SpringBoot项目对于Servlet组件的支持。 ServletComponentScan //开启对Servlet组件的支持 SpringBootApplication public class TliasManagementApplication {public static void main(String[] args) {SpringApplication.run(TliasManagementApplication.class, args);} } 重新启动服务打开浏览器执行部门管理的请求可以看到控制台输出了过滤器中的内容 注意事项在过滤器Filter中如果不执行放行操作将无法访问后面的资源。 放行操作chain.doFilter(request, response); 实现登录校验过滤器 分析 使用过滤器Filter来完成案例当中的登录校验功能。 对于校验令牌的这一块操作我们使用登录校验的过滤器在过滤器当中来校验令牌的有效性。如果令牌是无效的就响应一个错误的信息也不会再去放行访问对应的资源了。如果令牌存在并且它是有效的此时就会放行去访问对应的web资源执行相应的业务操作。 思考两个问题 对于所有的请求拦截到之后都需要校验令牌吗 答案登录请求不校验 拦截到请求后什么情况下才可以放行执行业务操作 答案有令牌且令牌校验通过(合法)否则都返回未登录错误结果 具体流程 要完成登录校验主要是利用Filter过滤器实现令牌校验Filter过滤器的实现流程如下 基于上面的业务流程我们分析出具体的操作步骤 获取请求的url 判断请求的url中是否包含login如果包含说明是登录操作放行 获取请求头中的令牌token 判断令牌是否存在如果不存在响应 401 解析token如果解析失败响应 401 放行 代码实现 在 com.itheima.filter 包下创建TokenFilter具体代码如下 package com.itheima.filter;import com.itheima.utils.JwtUtils; import jakarta.servlet.*; import jakarta.servlet.annotation.WebFilter; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.apache.http.HttpStatus; import org.springframework.util.StringUtils; import java.io.IOException;/*** 令牌校验过滤器*/ Slf4j WebFilter(urlPatterns /*) public class TokenFilter implements Filter {Overridepublic void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {HttpServletRequest request (HttpServletRequest) req;HttpServletResponse response (HttpServletResponse) resp;//1. 获取前端发送的请求路径。String url request.getRequestURL().toString();//2. 判断请求url中是否包含login如果包含说明是登录操作放行。if(url.contains(login)){ //登录请求log.info(登录请求 , 直接放行);chain.doFilter(request, response);return;}//3. 获取请求头中的令牌token。String jwt request.getHeader(token);//4. 判断令牌是否存在如果不存在说明没有登录返回错误结果未登录if(!StringUtils.hasLength(jwt)){ //jwt为空log.info(获取到jwt令牌为空, 返回错误结果);response.setStatus(HttpStatus.SC_UNAUTHORIZED);return;}//5. 如果token存在则解析token如果解析失败返回错误结果未登录try {JwtUtils.parseJWT(jwt);} catch (Exception e) {e.printStackTrace();log.info(解析令牌失败, 返回错误结果);response.setStatus(HttpStatus.SC_UNAUTHORIZED);return;}//6. 如果校验token通过则放行log.info(令牌合法, 放行);chain.doFilter(request , response);}} 登录校验的过滤器编写完成接下来就可以重新启动服务来做一个测试 测试1未登录是否可以访问部门管理页面 首先关闭浏览器重新打开浏览器在地址栏中输入http://localhost:90 由于用户没有登录登录校验过滤器返回错误信息前端页面根据返回的错误信息结果自动跳转到登录页面了 测试2先进行登录操作再访问部门管理页面 登录校验成功之后可以正常访问相关业务操作页面 Filter详解 主要介绍以下3个方面的细节 过滤器的执行流程 过滤器的拦截路径配置 过滤器链 执行流程 过滤器的执行流程 在过滤器中拦截到了请求之后如果希望继续访问后面的web资源需要执行放行操作放行操作就是调用 FilterChain对象当中的doFilter()方法调用doFilter()方法前编写的代码属于放行前的逻辑。 放行之后访问完 web 资源之后还会回到过滤器中回到过滤器之后如有需求还可以执行放行后的逻辑放行后的逻辑写在doFilter()这行代码后。 测试代码 WebFilter(urlPatterns /*) public class DemoFilter implements Filter {Override //初始化方法, 只调用一次public void init(FilterConfig filterConfig) throws ServletException {System.out.println(init 初始化方法执行了);}Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {System.out.println(DemoFilter 放行前逻辑.....);//放行请求filterChain.doFilter(servletRequest,servletResponse);System.out.println(DemoFilter 放行后逻辑.....);}Override //销毁方法, 只调用一次public void destroy() {System.out.println(destroy 销毁方法执行了);} } 启动之后运行测试 拦截路径 Filter可以根据需求配置不同的拦截路径 拦截路径urlPatterns值含义拦截具体路径/login只有访问 /login 路径时才会被拦截目录拦截/emps/*访问/emps下的所有资源都会被拦截拦截所有/*访问所有资源都会被拦截 下面我们来测试拦截具体路径 WebFilter(urlPatterns /login) //拦截/login具体路径 public class DemoFilter implements Filter {Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {System.out.println(DemoFilter 放行前逻辑.....);//放行请求filterChain.doFilter(servletRequest,servletResponse);System.out.println(DemoFilter 放行后逻辑.....);}Overridepublic void init(FilterConfig filterConfig) throws ServletException {Filter.super.init(filterConfig);}Overridepublic void destroy() {Filter.super.destroy();} } 过滤器链 过滤器链指的是在一个web应用程序中可以配置多个过滤器多个过滤器就形成了一个过滤器链。 比如在我们web服务器当中定义了两个过滤器这两个过滤器就形成了一个过滤器链。 而这个链上的过滤器在执行的时候会一个一个的执行会先执行第一个Filter放行之后再执行第二个Filter如果执行到最后一个过滤器放行后才会访问对应的web资源。 访问完web资源之后按照我们刚才所介绍的过滤器的执行流程还会回到过滤器当中来执行过滤器放行后的逻辑而在执行放行后的逻辑的时候顺序是反着的。 先要执行过滤器2放行后的逻辑再来执行过滤器1放行后的逻辑最后在给浏览器响应数据。 过滤器链上过滤器的执行顺序注解配置的Filter优先级是按照过滤器类名字符串的自然排序。 比如 AbcFilter DemoFilter 这两个过滤器来说AbcFilter 会先执行DemoFilter会后执行。 拦截器Interceptor 快速入门 是一种动态拦截方法调用的机制类似于过滤器。 拦截器是Spring框架中提供的用来动态拦截控制器方法的执行。 拦截器的作用拦截请求在指定方法调用前后根据业务需要执行预先设定的代码。 什么是拦截器 在拦截器当中我们通常也是做一些通用性的操作比如我们可以通过拦截器来拦截前端发起的请求将登录校验的逻辑全部编写在拦截器当中。在校验的过程当中如发现用户登录了(携带JWT令牌且是合法令牌)就可以直接放行去访问spring当中的资源。如果校验时发现并没有登录或是非法令牌就可以直接给前端响应未登录的错误信息。 下面通过快速入门程序来学习下拦截器的基本使用。拦截器的使用步骤和过滤器类似也分为两步 定义拦截器 注册配置拦截器 1). 自定义拦截器 定义一个类实现HandlerInterceptor接口并重写其所有方法 由于拦截器是Spring框架中提供的所以要交给Spring IOC容器管理。 //自定义拦截器 Component public class DemoInterceptor implements HandlerInterceptor {//目标资源方法执行前执行最终返回true放行 返回false不放行Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println(preHandle .... );return true; //true表示放行}//目标资源方法执行后执行Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println(postHandle ... );}//视图渲染完毕后执行最后执行Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {System.out.println(afterCompletion .... );} } 2). 注册配置拦截器 创建一个配置类 WebConfig 实现 WebMvcConfigurer 接口并重写其 addInterceptors 方法 在addInterceptor方法中使用InterceptorRegistry对象调用addInterceptor方法注册拦截器 Configuration public class WebConfig implements WebMvcConfigurer {//自定义的拦截器对象Autowiredprivate DemoInterceptor demoInterceptor;Overridepublic void addInterceptors(InterceptorRegistry registry) {//注册自定义拦截器对象并设置拦截器拦截的请求路径 /** 表示拦截所有请求registry.addInterceptor(demoInterceptor).addPathPatterns(/**);} } 重新启动SpringBoot服务打开Apifox测试 可以看到控制台输出的日志 接下来再来做一个测试将拦截器中preHandle方法的返回值改为false 使用Apifox再次发送请求并没有响应数据说明请求被拦截了但没有放行 令牌校验Interceptor 通过拦截器来完成登录校验功能 登录校验的业务逻辑以及操作步骤和登录校验Filter过滤器当中的逻辑是完全一致的。 1). TokenInterceptor 在 com.itheima.interceptor 包下创建 TokenInterceptor Slf4j Component public class TokenInterceptor implements HandlerInterceptor {Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//1. 获取请求url。String url request.getRequestURL().toString();//2. 判断请求url中是否包含login如果包含说明是登录操作放行。if(url.contains(login)){ //登录请求log.info(登录请求 , 直接放行);return true;}//3. 获取请求头中的令牌token。String jwt request.getHeader(token);//4. 判断令牌是否存在如果不存在返回错误结果未登录。if(!StringUtils.hasLength(jwt)){ //jwt为空log.info(获取到jwt令牌为空, 返回错误结果);response.setStatus(HttpStatus.SC_UNAUTHORIZED);return false;}//5. 解析token如果解析失败返回错误结果未登录。try {JwtUtils.parseJWT(jwt);} catch (Exception e) {e.printStackTrace();log.info(解析令牌失败, 返回错误结果);response.setStatus(HttpStatus.SC_UNAUTHORIZED);return false;}//6. 放行。log.info(令牌合法, 放行);return true;}} 2). 配置拦截器 Configuration public class WebConfig implements WebMvcConfigurer {//拦截器对象Autowiredprivate TokenInterceptor tokenInterceptor;Overridepublic void addInterceptors(InterceptorRegistry registry) {//注册自定义拦截器对象registry.addInterceptor(tokenInterceptor).addPathPatterns(/**);} }Interceptor详解 拦截器的拦截路径配置 拦截器的执行流程 拦截路径 首先看拦截器的拦截路径配置在注册配置拦截器的时候我们要指定拦截器的拦截路径通过addPathPatterns(要拦截路径)方法指定要拦截哪些请求路径。 在入门程序中我们配置的是/**表示拦截所有资源而在配置拦截器时不仅可以指定要拦截哪些资源还可以指定不拦截哪些资源只需要调用excludePathPatterns(不拦截路径)方法指定哪些请求路径不需要拦截。 Configuration public class WebConfig implements WebMvcConfigurer {//拦截器对象Autowiredprivate DemoInterceptor demoInterceptor;Overridepublic void addInterceptors(InterceptorRegistry registry) {//注册自定义拦截器对象registry.addInterceptor(demoInterceptor).addPathPatterns(/**)//设置拦截器拦截的请求路径 /** 表示拦截所有请求.excludePathPatterns(/login);//设置不拦截的请求路径} } 在拦截器中除了可以设置/**拦截所有资源外还有一些常见拦截路径设置 拦截路径含义举例/*匹配一级路径能匹配/depts/emps/login不能匹配 /depts/1/**匹配任意级路径能匹配/depts/depts/1/depts/1/2/depts/*匹配/depts下的一级路径能匹配/depts/1不能匹配/depts/1/2/depts/depts/**匹配/depts下的任意级路径能匹配/depts/depts/1/depts/1/2不能匹配/emps/1 执行流程 接下来再来介绍拦截器的执行流程。通过执行流程大家就能够清晰的知道过滤器与拦截器的执行时机。 当过滤器和拦截器同时存在时请求会先被过滤器拦截然后才会到拦截器因为拦截器是Spring框架提供的它只会拦截对Spring中的请求也就是要来访问我们所定义的controller中的接口方法。 过滤器和拦截器它们之间的区别主要是两点 接口规范不同过滤器需要实现Filter接口而拦截器需要实现HandlerInterceptor接口。 拦截范围不同过滤器Filter会拦截所有的资源而Interceptor只会拦截Spring环境中的资源。
http://www.hkea.cn/news/14562859/

相关文章:

  • 做淘宝美工图片网站手机网站可以做百度商桥吗
  • lamp做网站的论文个人对网络营销的看法
  • 做wordpress挣钱重庆网站seo
  • 预付做网站定金如何wordpress大学主题
  • 做网站记者好吗西宁市城市道路建设规划网站
  • 北京网站外包公司侯马市网站建设公司
  • 如何查找网站备案wordpress最新版获取标签
  • 玉树北京网站建设长春做网站的电话
  • 请人做网站要多数据库与网站建设
  • 流量统计是可以查询到网站来路的关键字里出现了不相关的关键词怎么样做贷款网站
  • IDC网站用什么软件建红色系网站设计
  • 网站密钥怎么做创意字体设计网站
  • 交换链接网站网站毕业设计任务书
  • 公司做网站广告语wordpress 免费 最好
  • 网站建设自学 优帮云网站优化首页付款
  • vue做网站一元钱购买网站空间
  • 个人网站的设计与实现招工信息58同城
  • wordpress的配置dns关键词优化的价格查询
  • 北京企业网站seo平台WordPress中文旅游主题
  • html模板网站模板下载贵阳有做网站的公司吗
  • 公司网站做推广支出分录seo优化是什么意思
  • 外贸网站建设ppt模板怎么做自己的购物网站
  • 美食网站页面设计源代码快速建站公司有哪些
  • 程序员做的导航网站制作网页的基本技术标准
  • 美词原创网站建设做网站前期预算
  • 手机做点击赚钱的网站网线制作实训报告心得体会
  • 南京中天园林建设网站北京装修公司十大排名
  • 怎么用ps做购物网站免费开发网站大全
  • 如何自建网站做淘客南宁优质手机网站建设公司
  • 哪个网站有手机运营最好的网站