后端登入认证

发布于:2025-07-07 ⋅ 阅读:(39) ⋅ 点赞:(0)

登入:

创建一个实体类来封装登入的信息

package org.example.threelayerdecouplingdomeapplication2.pojo;

public class LoginInfo {
    private Integer id;
    private String username;
    private String name;
    private String token;

    public LoginInfo() {
    }

    public LoginInfo(Integer id, String username, String name, String token) {
        this.id = id;
        this.username = username;
        this.name = name;
        this.token = token;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getToken() {
        return token;
    }

    public void setToken(String token) {
        this.token = token;
    }
}

接受前端返回登入的账号密码,直接用Emp封装

package org.example.threelayerdecouplingdomeapplication2.controller;

import org.example.threelayerdecouplingdomeapplication2.pojo.Emp;
import org.example.threelayerdecouplingdomeapplication2.pojo.LoginInfo;
import org.example.threelayerdecouplingdomeapplication2.pojo.Result;
import org.example.threelayerdecouplingdomeapplication2.service.impl.EmpServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/login")
public class LoginController {
    @Autowired
    EmpServiceImpl empServiceImpl;
    @PostMapping
    public Result login(@RequestBody Emp emp) {
        LoginInfo login = empServiceImpl.login(emp);
        if(login != null) {
            return Result.ok(login);
        }
        else
            return Result.error("用户名或密码错误");
    }
}

servers层进行数据处理

public LoginInfo login(Emp emp) {
        Emp emp1 = empMapper.selectByUsernameAndPassword(emp);
        if(emp1 != null)
        {
            return new LoginInfo(emp1.getId(),emp1.getUsername(),emp1.getName(),"");
        }else
        {
            return null;
        }
    }

使用用户名和密码进行进行查询操作

Emp selectByUsernameAndPassword(Emp emp);
<select id="selectByUsernameAndPassword"
            resultType="org.example.threelayerdecouplingdomeapplication2.pojo.Emp">
        select id ,username,name from emp where username=#{username} and password=#{password};
    </select>

校验:

http协议是无状态的协议,请求的时候无法携带之前请求的结果,就需要对用户是否登入进行校验

两种方式进行校验 

1·登录标记:用户登录成功之后,在后续的每一次请求中,都可以获取到该标记。【会话技术】
2·统一拦截:过滤器Filter、拦截器Interceptor

会话技术

会话:用户打开浏览器,访问web服务器的资源,会话建立,直到有一方断开连接,会话结束。在一次会话中可以包含多次请求和响

会话跟踪:一种维护浏览器状态的方法,服务器需要识别多次请求是否来自于同一浏览器,以便在同一次会话的多次请求间共享数据。

会话跟踪方案:
·客户端会话跟踪技术:Cookie
·服务端会话跟踪技术:Session
·令牌技术

Cookie

Cookie 是由服务器生成并发送到客户端浏览器的一小段文本数据,存储在用户本地计算机上。当浏览器再次访问同一服务器时,会将相应的 Cookie 发送回服务器,从而实现会话状态的跟踪。

1. 交互流程
  1. 客户端首次访问服务器,服务器生成 Cookie 并通过响应头 Set-Cookie 返回
  2. 浏览器将 Cookie 存储在本地
  3. 后续请求中,浏览器自动在请求头 Cookie 中携带对应 Cookie 发送给服务器
  4. 服务器解析 Cookie 数据,处理用户请求
2. Cookie 组成要素
  • 名称(Name):Cookie 的标识
  • 值(Value):存储的具体数据
  • 过期时间(Expires/Max-Age):Cookie 的有效期
  • 路径(Path):指定 Cookie 生效的 URL 路径
  • 域名(Domain):指定 Cookie 生效的域名
  • Secure:标记是否仅通过 HTTPS 传输
  • HttpOnly:标记是否禁止 JavaScript 访问

Session

1. 核心作用
  • 状态保持:在无状态的 HTTP 协议中,通过 Session 为每个用户维护独立的会话状态
  • 数据存储:将用户信息(如登录状态、购物车)存储在服务器端
  • 安全增强:相比 Cookie,Session 数据存储在服务器,避免敏感信息泄露
2. 与 Cookie 的关系
  • Session ID:服务器生成的唯一标识符,通过 Cookie(默认名为 JSESSIONID)发送到客户端
  • 依赖关系:Session 依赖 Cookie 传递 Session ID,若无 Cookie 支持(如禁用 Cookie),需通过 URL 重写等方式传递 Session ID

令牌

JWT令牌

全称:JSON Web Token (https: //jwt.io/)
定义了一种简洁的、自包含的格式,用于在通信双方以json数据格式安全的传输信息。组成:
第一部分: Header(头),记录令牌类型、签名算法等。例如: { "alg" : "HS256" , "type " : "JWT"}
第二部分: Payload(有效载荷),携带一些自定义信息、默认信息等。例如: {"id": "1" , "username" : "Tom"}]
第三部分: Signature(签名),防止Token被篡改、确保安全性。将header、 payload融入,并加入指定秘钥,通过指定签名算法计算而来。

生成/解析

引入jjwt的依赖。


调用官方提供的工具类Jwts来生成或解析jwt令牌。

生成的工具类
package org.example.threelayerdecouplingdomeapplication2.utils;

import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

import java.util.Date;
import java.util.Map;

/**
 * JWT工具类,用于生成和解析JSON Web Token
 * 基于io.jsonwebtoken库实现,提供了Token的创建与验证功能
 * 
 * JWT token格式: header.payload.signature
 * - header: 包含签名算法和token类型
 * - payload: 包含声明(claims),如用户ID、角色等
 * - signature: 使用密钥对header和payload进行签名
 */
public class JwtUtils {

    /**
     * 生成JWT Token
     * @param claims 存储在JWT payload中的声明信息,通常包含用户ID、角色等
     * @return 生成的JWT字符串
     * 
     * 示例:
     * Map<String, Object> claims = new HashMap<>();
     * claims.put("userId", 123);
     * claims.put("role", "admin");
     * String token = JwtUtils.generateToken(claims);
     */
    public static String generateToken(Map<String, Object> claims) {
        return Jwts.builder()
                // 使用HS256算法和指定密钥进行签名
                .signWith(SignatureAlgorithm.HS256, "weiyicheng")
                // 将用户自定义的声明信息添加到payload中
                .addClaims(claims)
                // 设置Token的过期时间为7天
                .setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 24 * 7))
                // 生成最终的JWT字符串
                .compact();
    }

    /**
     * 解析JWT Token,获取其中的声明信息
     * @param token 待解析的JWT字符串
     * @return 包含声明信息的Map
     * @throws Exception 当Token无效、过期或签名验证失败时抛出异常
     * 
     * 示例:
     * Map<String, Object> claims = JwtUtils.parseToken(token);
     * Integer userId = (Integer) claims.get("userId");
     */
    public static Map<String, Object> parseToken(String token) throws Exception {
        return Jwts.parser()
                // 设置用于验证签名的密钥,必须与生成Token时使用的密钥相同
                .setSigningKey("weiyicheng")
                // 解析JWT并验证签名和过期时间
                .parseClaimsJws(token)
                // 获取JWT中的payload部分(即声明信息)
                .getBody();
    }
}

过滤器Filter

概念:Filter过滤器,是JavaWeb三大组件(ServletFilter、Lisner)之一。
过滤器可以把对资源的请求拦截下来,从而实现一些特殊的功能。
过滤器一般完成一些通用的操作,比如∶登录校验、统一编码处理、敏感字符处理等。

快速入门:

1. 定义Filter:定义一个类,实现 Filter接口,并实现其所有方法。
2.配置Filter: Filter类上加oMebFilter注解,配置拦截路径。引导类上加@ServletComponentScan开启Servlet组件

package org.example.threelayerdecouplingdomeapplication2.filter;

import jakarta.servlet.*;
import jakarta.servlet.annotation.WebFilter;

import java.io.IOException;

/**
 * DemoFilter 是一个Servlet过滤器,用于拦截所有HTTP请求
 * 过滤器是Java Web应用中实现AOP(面向切面编程)的重要组件
 * 可用于实现权限检查、字符编码转换、日志记录等横切关注点
 */
@WebFilter(urlPatterns = "/*")  // 指定该过滤器拦截所有URL请求
public class DemoFilter implements Filter {
    
    /**
     * 过滤器初始化方法,在过滤器实例创建后调用
     * 通常用于加载配置信息、初始化资源等操作
     * @param filterConfig 过滤器配置对象,包含初始化参数和Servlet上下文
     * @throws ServletException 初始化过程中发生的Servlet异常
     */
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // 调用父类初始化方法(默认实现为空)
        Filter.super.init(filterConfig);
    }

    /**
     * 过滤器核心方法,每次请求匹配URL模式时都会调用
     * 可对请求和响应进行预处理和后处理
     * @param servletRequest 客户端请求对象
     * @param servletResponse 服务器响应对象
     * @param filterChain 过滤器链,用于传递请求到下一个过滤器或Servlet
     * @throws IOException I/O异常
     * @throws ServletException Servlet处理异常
     */
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        // 1. 前置处理代码可以放在这里(例如设置字符编码)
        
        // 2. 调用filterChain.doFilter传递请求到下一个组件
        //    如果是最后一个过滤器,则传递到目标Servlet
        filterChain.doFilter(servletRequest, servletResponse);
        
        // 3. 后置处理代码可以放在这里(例如记录响应信息)
    }

    /**
     * 过滤器销毁方法,在过滤器实例被销毁前调用
     * 通常用于释放资源、关闭连接等清理操作
     */
    @Override
    public void destroy() {
        // 调用父类销毁方法(默认实现为空)
        Filter.super.destroy();
    }
}
令牌校验:

package org.example.threelayerdecouplingdomeapplication2.filter;

import jakarta.servlet.*;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.example.threelayerdecouplingdomeapplication2.utils.JwtUtils;

import java.io.IOException;

/**
 * Token过滤器 - 用于验证HTTP请求中的JWT令牌
 * 该过滤器会拦截所有请求,对需要认证的接口进行令牌校验
 * 实现了基于JWT的无状态认证机制
 */
@WebFilter(urlPatterns = "/*")
public class TokenFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        // 将通用请求/响应对象转换为HTTP专用对象
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        
        // 获取请求URI,用于判断是否为无需认证的接口
        String requestURI = request.getRequestURI();
        
        // 设置响应字符编码为UTF-8,防止中文乱码
        response.setCharacterEncoding("UTF-8");
        
        // 白名单处理:登录和注册接口无需认证,直接放行
        if (requestURI.contains("/login") || requestURI.contains("/register")) {
            filterChain.doFilter(request, response); // 放行请求到后续过滤器或Servlet
            return;
        }
        
        // 从请求头中获取JWT令牌
        String token = request.getHeader("token");
        
        // 令牌存在性检查
        if (token != null && !token.isEmpty()) {
            try {
                // 调用工具类解析令牌:验证签名有效性、过期时间等
                JwtUtils.parseToken(token);
                
                // 令牌验证通过,放行请求
                filterChain.doFilter(request, response);
            } catch (Exception e) {
                // 令牌验证失败,返回401状态码和错误信息
                response.getWriter().write("非法令牌");
                response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            }
        } else {
            // 未提供令牌,返回401状态码和错误信息
            response.getWriter().write("未登入");
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            return;
        }
    }
}
Filter-过滤器链

介绍:一个web应用中,可以配置多个过滤器,这多个过滤器就形成了一个过滤器链。
顺序:注解配置的Filter,优先级是按照过滤器类名(字符串)的自然排序。

拦截器

概念:是一种动态拦截方法调用的机制,类似于过滤器。Spring框架中提供的,主要用来动态拦截控制器方法的执行。作用:拦截请求,在指定的方法调用前后,根据业务需要执行预先设定的代码。

快速入门

1·定义拦截器,实现HandlerInterceptor接口,并实现其所有方法。

2·注册拦截器

令牌校验:

1·获取请求url。
2·判断请求url中是否包含login,如果包含,说明是登录操作,放行
3·获取请求头中的令牌( token)。
4·判断令牌是否存在,如果不存在,响应401。
5·解析token,如果解析失败,响应401 。
6·放行。

package org.example.threelayerdecouplingdomeapplication2.interceptor;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.example.threelayerdecouplingdomeapplication2.utils.JwtUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
@Component
public class TokenInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String requestURI = request.getRequestURI();
        if (requestURI.contains("/login") || requestURI.contains("/register")) {

            return true;
        }
        String token = request.getHeader("token");
        response.setCharacterEncoding("UTF-8");
        if (token != null&& !token.isEmpty()) {
            try {
                JwtUtils.parseToken(token);
                return true;
            } catch (Exception e) {
                response.getWriter().write("非法令牌");
                response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            }
        }
        else {
            response.getWriter().write("未登入");
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            return false;
        }
        return false;
    }
}
详情

配置时指定拦截器对象,添加拦截路径(“/**”)和排除拦截的路径(“/login”)

配置拦截路径

过滤器和拦截器同时存在的时候先经过过滤器在通过拦截器


网站公告

今日签到

点亮在社区的每一天
去签到