1. 登录认证
1. 登录功能
1.1 需求
在登录界面中,我们可以输入用户的用户名以及密码,然后点击 “登录” 按钮就要请求服务器,服务端判断用户输入的用户名或者密码是否正确。如果正确,则返回成功结果,前端跳转至系统首页面。
1.2 功能实现
1.2.1 前端页面
- 登录页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>login</title>
</head>
<body>
<form action="/login" method="post">
账号: <input type="text" name="name"><br>
密码: <input type="password" name="age"><br>
<input type="submit" value="登录">
</form>
</body>
</html>
- 首页
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>index</title>
</head>
<body>
主页
</body>
</html>
-
Java部分

-
测试结果

2. 登录校验
2.1 问题分析
- 当我们知道系统后台url时,可跳过登录直接进入系统,因此需进行登录校验
- 所谓登录校验,指的是我们在服务器端接收到浏览器发送过来的请求之后,首先我们要对请求进行校验。先要校验一下用户登录了没有,如果用户已经登录了,就直接执行对应的业务操作就可以了;如果用户没有登录,此时就不允许他执行相关的业务操作,直接给前端响应一个错误的结果,最终跳转到登录页面,要求他登录成功之后,再来访问对应的数据。
- 我们可以添加拦截器来拦截前端请求,以达到以上目的;但拦截器会一直拦截请求,导致用户每次发送请求都需要校验,体验极差;为此,我们需使用会话技术,让用户登陆后可以发起多次请求而不进行校验。
2.2 会话技术
- 在web开发当中,会话指的就是浏览器与服务器之间的一次连接,我们就称为一次会话
- 会话跟踪:一种维护浏览器状态的方法,服务器需要识别多次请求是否来自于同一浏览器,以便在同一次会话的多次请求间共享数据
- 会话跟踪技术有两种:
- Cookie(客户端会话跟踪技术)
- 数据存储在客户端浏览器当中
- 优点:HTTP协议中支持的技术(像Set-Cookie 响应头的解析以及 Cookie 请求头数据的携带,都是浏览器自动进行的,是无需我们手动操作的)
- 缺点:
- 移动端APP(Android、IOS)中无法使用Cookie
- 不安全,用户可以自己禁用Cookie
- Cookie不能跨域
- Session(服务端会话跟踪技术)
- 数据存储在储在服务端
- 优点:Session是存储在服务端的,安全
- 缺点:
- 服务器集群环境下无法直接使用Session
- 移动端APP(Android、IOS)中无法使用Cookie
- 用户可以自己禁用Cookie
- Cookie不能跨域
- 令牌技术
- 优点:
- 支持PC端、移动端
- 解决集群环境下的认证问题
- 减轻服务器的存储压力(无需在服务器端存储)
- 缺点:需要自己实现(包括令牌的生成、令牌的传递、令牌的校验)
2.3 JWT令牌
-
JWT全称:JSON Web Token (官网:https://jwt.io/)
-
JWT的组成: (JWT令牌由三个部分组成,三个部分之间使用英文的点来分割)
- 第一部分:Header(头), 记录令牌类型、签名算法等。 例如:{“alg”:“HS256”,“type”:“JWT”}
- 第二部分:Payload(有效载荷),携带一些自定义信息、默认信息等。 例如:{“id”:“1”,“username”:“Tom”}
- 第三部分:Signature(签名),防止Token被篡改、确保安全性。将header、payload,并加入指定秘钥,通过指定签名算法计算而来。
2.3.1 生成和校验
- 导入依赖
<!-- JWT依赖-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
生成JWT代码实现:
@Test
public void genJwt(){
Map<String,Object> claims = new HashMap<>();
claims.put("id",1);
claims.put("username","Tom");
String jwt = Jwts.builder()
.setClaims(claims) //自定义内容(载荷)
.signWith(SignatureAlgorithm.HS256, "itheima") //签名算法
.setExpiration(new Date(System.currentTimeMillis() + 24*3600*1000)) //有效期
.compact();
System.out.println(jwt);// eyJhbGciOiJIUzI1NiJ9.eyJpZCI6MSwiZXhwIjoxNjcyNzI5NzMwfQ.fHi0Ub8npbyt71UqLXDdLyipptLgxBUg_mSuGJtXtBk
}
实现了JWT令牌的生成,下面我们接着使用Java代码来校验JWT令牌(解析生成的令牌):
@Test
public void parseJwt(){
Claims claims = Jwts.parser()
.setSigningKey("itheima")//指定签名密钥(必须保证和生成令牌时使用相同的签名密钥)
.parseClaimsJws("eyJhbGciOiJIUzI1NiJ9.eyJpZCI6MSwiZXhwIjoxNjcyNzI5NzMwfQ.fHi0Ub8npbyt71UqLXDdLyipptLgxBUg_mSuGJtXtBk")
.getBody();
System.out.println(claims);
}
2.4 过滤器Filter
- Filter表示过滤器,是 JavaWeb三大组件(Servlet、Filter、Listener)之一。
- 过滤器可以把对资源的请求拦截下来,从而实现一些特殊的功能
- 使用了过滤器之后,要想访问web服务器上的资源,必须先经过滤器,过滤器处理完毕之后,才可以访问对应的资源。
- 过滤器一般完成一些通用的操作,比如:登录校验、统一编码处理、敏感字符处理等。
3. 实现登录认证
- 结合过滤器Filter 和JWT令牌,为系统实现登录认证
- 编写过滤器
package com.practice.filter;
import com.alibaba.fastjson.JSONObject;
import com.practice.pojo.Result;
import io.jsonwebtoken.*;
import org.springframework.util.StringUtils;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebFilter("/*")
public class LoginCheckFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//前置:强制转换为http协议的请求对象、响应对象 (转换原因:要使用子类中特有方法)
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
//1.获取请求url
String url = request.getRequestURL().toString();
System.out.println("拦截生效" + url);
//2.判断请求url中是否包含login,如果包含,说明是登录操作,放行
if (url.contains("/login")) {
filterChain.doFilter(servletRequest, servletResponse);
}else if (StringUtils.hasLength(request.getHeader("token"))){
//3.获取请求头中的令牌(token)
//4.判断令牌是否存在(长度是否为0),如果不存在,返回错误结果(未登录)
String r = JSONObject.toJSONString(Result.error("Please Login"));
response.getWriter().write(r);
} else {
//5.解析token,如果解析失败,返回错误结果(未登录)
try {
Claims claims = Jwts.parser()
.setSigningKey("lang")//指定签名密钥
.parseClaimsJws(request.getHeader("token"))//指定令牌Token
.getBody();
//6.如果解析失败不会执行到此语句,放行
filterChain.doFilter(servletRequest, servletResponse);
} catch (Exception e) {
String r = JSONObject.toJSONString(Result.error("Please Login"));
response.getWriter().write(r);
}
}
}
}
- 编写登录校验
@RestController
public class Login {
@Autowired
AccountService as;
@PostMapping("/login")
Result login(@RequestBody Account account){
Account result = as.login(account);
if (result != null){
HashMap<String, Object> claims = new HashMap<>();
claims.put("username", account.getName());
String jwt = Jwts.builder()
.addClaims(claims)//自定义信息(有效载荷)
.signWith(SignatureAlgorithm.HS256, "lang")//签名算法(头部)
.setExpiration(new Date(System.currentTimeMillis() + 3600*1000))//过期时间
.compact();
return Result.success();
}
return Result.error("用户名或密码错误!");
}
@PostMapping("/test")
void a(){}
}
-
测试结果
-
拦截没有令牌的请求

-
登录后下发令牌

文章详细介绍了Web应用中实现登录认证的过程,包括前端登录页面的创建、后端的登录校验需求分析以及会话技术的讨论。重点讲解了JWT(JSONWebToken)的使用,包括其组成部分、生成和校验方法。同时,提到了过滤器Filter在登录认证中的作用,如何通过Filter拦截请求并进行JWT令牌的验证。最后,给出了LoginCheckFilter的实现示例和登录接口的代码片段。
1179

被折叠的 条评论
为什么被折叠?



