Java 登录专题

核心背景:HTTP 是无状态的

服务器默认不记得你是谁

  • 请求1 ------ 完了就忘
  • 请求2 ------ 当新请求

在客户端保存状态标识,解决 HTTP 无状态问题

java 复制代码
@GetMapping("/hello")
public String hello(HttpServletResponse response) {
    Cookie cookie = new Cookie("name", "value");
    // 20秒后过期
    cookie.setMaxAge(20);
    response.addCookie(cookie);
    return "OK";
}

@GetMapping("/user")
public void user(HttpServletRequest request) {
    Cookie[] cookies = request.getCookies();
    for (Cookie cookie : cookies) {
        if (cookie.getName().equals("name")) {
            System.out.println(cookie.getName() + ":" + cookie.getValue());
        }
    }
}

后端设置 cookie 时

前端发起请求携带 cookie 时

cookie 管理界面

优缺点

优点:HTTP协议中支持的技术

缺点:

  • 移动端APP无法使用Cookie
  • 不安全,用户可以自己禁用Cookie
  • Cookie不能跨域

Session

HttpSession 是服务器端保存用户会话状态的对象

核心特点:

  • 存在服务器内存(或 Redis)
  • 以 SessionId 为索引
  • 解决 HTTP 无状态问题

Session 是怎么连续的?(核心原理)

第一次访问之后,会产生一个Cookie,往后访问都会带着它

java 复制代码
// 存值
@GetMapping("/hello")
public String hello(HttpSession session) {
    log.info("HttpSession-s1:{}", session.hashCode());
    session.setAttribute("name", "hello");
    return "OK";
}

    // 获取值
@GetMapping("/user")
public void user(HttpSession session) {
    log.info("HttpSession-s2:{}", session.hashCode());
    Object name = session.getAttribute("name");
    log.info("name: {}", name);
}

缺点,分布式集群下无法使用 Session

优点:数据存放到服务端

令牌JWT

令牌会话跟踪方案的优缺点 ?

优点:

  • 支持PC端、移动端
  • 解决集群环境下的认证问题
  • 减轻服务器端存储压力

缺点:需要自己实现

全称:JSON Web Token (jwt.io/)

定义了一种简洁的、自包含的格式,用于在通信双方以json数据格式安全的传输信息。 组成:

  • 第一部分:Header(头),记录令牌类型、签名算法等。 例如:{"alg":"HS256","type":"JWT"}
  • 第二部分:Payload(有效载荷),携带一些自定义信息、默认信息等。 例如:{"id":"1","username":"Tom"}
  • 第三部分:Signature(签名),防止Token被篡改、确保安全性。将header、payload融入,并加入指定密钥,通过指定签名算法计算而来。

Base64:是一种基于64个可打印字符(A-Z a-z 0-9 + /)来表示二进制数据的编码方式。

引入 maven

xml 复制代码
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-api</artifactId>
    <version>0.12.6</version>
</dependency>
java 复制代码
HashMap<String, Object> map = new HashMap<>();
map.put("id", 1);
map.put("name", "admin");

// 字符串中应该,base64机密的,加密内容必须足够长
String jwt = Jwts.builder().signWith(SignatureAlgorithm.HS256,
                "abcdefghijklmn")
        // 自定义数据
        .addClaims(map)
        // 过期时间, 单位毫秒 + 1小时
        .setExpiration(new Date(System.currentTimeMillis() + 3600 * 1000))
        .compact();

return jwt;

打印 JWT,用三个 . 分出来三个部分

eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiYWRtaW4iLCJpZCI6MSwiZXhwIjoxNzY2MjI3Njk5fQ.ZBiknENQ1NoKxv4myhVF2uS-toijk90uXz9O70Mhg9s

  1. 第一个部分,base64 解密是:{"alg":"HS256"}
  2. 第二部分,就是我们存入的对象
  3. 第三部分,就是加密字符串

令牌生成

java 复制代码
HashMap<String, Object> map = new HashMap<>();
    map.put("id", 1);
    map.put("name", "admin");

    // 字符串中应该,base64加密的,加密内容必须足够长
    String jwt = Jwts.builder().signWith(SignatureAlgorithm.HS256,
                    "abcdefghijklmn")
            // 自定义数据
            .addClaims(map)
            // 过期时间, 单位毫秒 + 1小时
            .setExpiration(new Date(System.currentTimeMillis() + 3600 * 1000))
            .compact();

令牌解析

java 复制代码
String token = "eyJhbGciOiJIUzI1NiJ9...";
// claims 实际就是 map
Claims claims = Jwts.parser().setSigningKey("abcdefghijklmn")
        .parseClaimsJws(token).getBody();

过滤器

多个过滤器是按照姓名

java 复制代码
// 开启SpringBoot 对 Servlet 组件的扫描
@ServletComponentScan
@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

}

配置过滤器

java 复制代码
@WebFilter(urlPatterns = "/*")
public class DemoFilter implements Filter {

    // Web 服务器启动时调用
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        Filter.super.init(filterConfig);
    }

    // 拦截请求
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

        // 放行,不写就会被拦截
        chain.doFilter(request, response);

    }

    // Web 服务器关闭时调用
    @Override
    public void destroy() {
        Filter.super.destroy();
    }
}

拦截器

配置

java 复制代码
@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new DemoInterceptor())
                // 拦截所有请求
                .addPathPatterns("/**")
                // 不拦截的请求
                .excludePathPatterns("/login");
    }
}

拦截器

java 复制代码
public class DemoInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("在请求之前到,true 返回,false 返回");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("目标运行之后运行");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("请求彻底结束");
    }
}
相关推荐
LiaCode1 小时前
Redis 在生产项目的使用
前端·后端
用户559822481221 小时前
Docker Compose Down 导致容器数据误删——ext4 日志恢复全记录
后端
LiaCode1 小时前
一天学完 redis 的爽翻版核心知识总结
前端·后端
大刚测试开发实战1 小时前
如何内网穿透访问本地私有化部署的TestHub
前端·后端·github
xiaodaoluanzha1 小时前
迄今為止,最簡單的編程語言 Nolang
前端·后端
Csvn1 小时前
Docker 容器管理入门 — 从镜像到容器编排
后端
用户762352425911 小时前
ShardingJDBC
后端
行者全栈架构师1 小时前
IDEA 中 Maven 项目的 15 个红色报错快速解决方法
java·后端
令人头秃的代码0_01 小时前
mac(m5)平台编译openjdk
java
Colin草率地做慢慢地改2 小时前
关于QuickStore这个项目的重构(2)- 数据库建表文件
后端·面试·架构