谈到 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 两种认证方式,虽然比较粗糙,但对于我们熟悉流程有用。