Java Spring Boot 集成 Cookie/Session 认证

565 阅读3分钟

谈到 auth ,很多人估计直接就冒出多种方式,常见的就是:

  • cookie
  • session
  • JWT

尽管现在用的比较多的是基于 JWT,比较有很多天然的优势。

本文重点不在讨论各种认证方式的优劣,这里主要熟悉在 Spring Boot 如何集成 Cookie 和 Session 认证。

环境:

  • Spring Boot:3.1.16
  • JDK:17

依赖

我们在项目中会应用到 redis 作为缓存,另外对请求参数做校验,所以我们主要用到如下依赖:

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

主配置文件

配置文件主要配置了 redis 的相关选项。

server:
  port: 8080
## redis
spring:
  data:
    redis:
      host: 172.xx.xx.xxx
      port: 6379
      database: 5
      timeout: 30000
      jedis:
        pool:
          max-active: 8
          max-idle: 8
          min-idle: 0
          max-wait: -1

用户类

我们对用户类就是简单的用户名和密码,就是一个简单 bean。

package com.example.springbootauthdemo.param;

import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;

public class User {
    @NotBlank
    private String username;

    @Min(value = 6)
    private String password;

    public String getUsername() {
        return username;
    }

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

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        return String.format("User:{username: %s}", username);
    }
}

redis的相关配置

请参考上篇关于 jedis 应用

Cookie认证

对于认证过来的请求,对传入参数进行校验后,如果通过校验,生成 cookie,设置 ttl,前端拿到我们的响应后,可以解析到 cookie,下次请求自动带上 cookie。

package com.example.springbootauthdemo.controller;

import com.example.springbootauthdemo.param.User;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class LoginUseCookieController {

    @PostMapping("/v2/login")
    public Object login(@RequestBody User user, HttpServletResponse response) {
        // validate user info

        // generate cookie
        Cookie cookie = new Cookie("username", "Aden");
        cookie.setMaxAge(3600);
        response.addCookie(cookie);
        return "Login is ok.";
    }
}

比如这次请求已经认证过,请求其他接口:

package com.example.springbootauthdemo.controller;

import com.example.springbootauthdemo.param.User;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class UserUseCookieController {

    @PostMapping("/v2/user/update/cookieValue")
    public Object update(@CookieValue(name = "username", required = true) String cookieValue, @RequestBody User user) {
        return cookieValue;
    }

    @PostMapping("/v2/user/update/hsr")
    public Object update(HttpServletRequest request, HttpServletResponse response) {
        Cookie[] cookies = request.getCookies();
        System.out.println(cookies);

        return "ok";
    }
}

这里我们也有多种获取 cookie 方式。

Session认证

在 session 认证中,因为涉及到 后端存储 session 问题,另外 session 也有时效性,所以我们这里集成了 redis 作为缓存。

package com.example.springbootauthdemo;

import com.example.springbootauthdemo.param.User;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import redis.clients.jedis.Jedis;

import java.util.UUID;

@RestController
public class LoginUseSessionController {

    @Autowired
    private JedisUtil jedisUtil;

    private static long ttl = 3600; // 3600s

    @PostMapping("/login/session/in")
    public Object login(HttpServletRequest request, HttpServletResponse response, @RequestBody User user) {
        Jedis jedis = jedisUtil.getJedis();
        // 非第一次登录
        String s = request.getHeader("sessionId");
        if (s != null && jedis.get(s) != null) {
            return "login again.";
        }
        if (user.getUsername().equals("admin") && user.getPassword().equals("12345678")) {
            // set session and cache session in redis
            String sessionId = UUID.randomUUID().toString();
            jedis.set(sessionId, user.toString());
            jedis.expire(sessionId, ttl);
            response.addHeader("sessionId", sessionId);
            return "login success!";
        }
        return "login failed!";
    }

    @DeleteMapping("/login/session/out")
    public Object logout(HttpServletRequest request) {
        Jedis jedis = jedisUtil.getJedis();
        String s = request.getHeader("sessionId");
        if (jedis.get(s) != null) {
            jedis.del(s);

        }

        return "logout success!";
    }
    
}

这里简单实现了登陆和登出功能,其中认证时,先看看有没有带 session,如果携带而且还是有效的,可以认为是重复登陆,当然比较成熟的方案应该是通过 拦截件 处理,这里主要演示 session认证,就在接口中处理了。

如果请求参数结合后端数据比较通过后,我们就可以生成 session,并缓存到 redis 中,随后返回响应。

以上就是基于 cookie/session 两种认证方式,虽然比较粗糙,但对于我们熟悉流程有用。