JavaWeb笔记_12

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

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开发当中,会话指的就是浏览器与服务器之间的一次连接,我们就称为一次会话
  • 会话跟踪:一种维护浏览器状态的方法,服务器需要识别多次请求是否来自于同一浏览器,以便在同一次会话的多次请求间共享数据
  • 会话跟踪技术有两种:

  1. Cookie(客户端会话跟踪技术)
    • 数据存储在客户端浏览器当中
  • 优点:HTTP协议中支持的技术(像Set-Cookie 响应头的解析以及 Cookie 请求头数据的携带,都是浏览器自动进行的,是无需我们手动操作的)
  • 缺点:
    • 移动端APP(Android、IOS)中无法使用Cookie
    • 不安全,用户可以自己禁用Cookie
    • Cookie不能跨域

  1. Session(服务端会话跟踪技术)
    • 数据存储在储在服务端
  • 优点:Session是存储在服务端的,安全
  • 缺点:
    • 服务器集群环境下无法直接使用Session
    • 移动端APP(Android、IOS)中无法使用Cookie
    • 用户可以自己禁用Cookie
    • Cookie不能跨域

  1. 令牌技术
  • 优点:
    • 支持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(){}
}
  • 测试结果

  • 拦截没有令牌的请求
    在这里插入图片描述

  • 登录后下发令牌
    在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值