Session
基础介绍
-
Session是程序员设置,保存在服务器上的用户信息,以键值对形式存在。不同用户(终端)的信息相互独立。通常结束访问(关闭浏览器)时消失。也可以调用方法主动删除。
-
在使用Session时,客户端浏览器保存一个cookie,内容为SessionID。每次请求访问服务器资源时,都会带上这个id。服务器设置、保存、查询这个id对应的一切。
-
服务器端通过控制器的方法参数,如
(HttpSession session),获取用户Session。 -
通过
session.setAttribute(key,value);设置;
通过session.getAttribute(key);读取;
通过session.removeAttribute(key);删除;
通过session.invalidate();使Session失效。
使用场景及注意事项
-
Session适合用在单服务器场景下,尤其适合权限鉴定,写法简单。
-
对安全性有要求的场景,应配合一些设置或技术(如http-only--默认已打开、Spring Security),降低安全风险。
-
多服务器(分布式、微服务)环境下,由于不能跨服务器,Session需要配合其他技术才能使用(如Redis服务器)。
-
高并发或多服务器环境下,JWT方式也是一种主流的认证方式,应合理选用技术。
例子
功能:
页面h1.html:输入用户名,登录。用户名长度超过4则登录成功。
页面h2.html:向服务器请求内容并展示。服务器根据登录状态,返回不同内容。
按照Spring boot MVC的约定,html和js都放在src\main\resources\static。
h1.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>h1</title>
<script src="./js/j1.js" type="module"></script>
</head>
<body>
用户名:<input type="text" id="t1"><input type="button" id="b1" value="提交">
</body>
</html>
j1.js:
async function submitUserName() {
let inputT1 = document.querySelector('#t1');
let btnB1 = document.querySelector('#b1');
let username = inputT1.value.trim();
if (!username) {
alert('用户名不能为空');
return;
}
try {
let res = await fetch('/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
username: username
})
});
let data = await res.text();
alert(data);
} catch (err) {
console.error(err);
alert('提交出错:' + err.message);
}
}
document.querySelector('#b1').addEventListener('click', submitUserName);
h2.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>h2</title>
<script src="./js/j2.js" type="module"></script>
</head>
<body>
<p>hi</p>
</body>
</html>
j2.js
async function loadString() {
let pDom = document.querySelector('p');
try {
let resp = await fetch('/getstring', {
method: 'GET',
});
let text = await resp.text();
pDom.textContent = text;
} catch (err) {
console.error(err);
}
}
loadString();
java端的控制器,写在src\main\java\com\example\demo_web\Controllers里。
My_Controller.java:
package com.example.demo_web.Controllers;
import java.util.Map;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import jakarta.servlet.http.HttpSession;
@RestController
public class My_Controller {
@PostMapping("/login")
public String log_In(@RequestBody Map<String,String> body,HttpSession session)
{
String username=body.get("username");
if(username.length()>4)
{
session.setAttribute("un", username);
return "OK";
}
else
{
if(session!=null)
{
session.invalidate();
}
return "No";
}
}
@GetMapping("getstring")
public String getSth(HttpSession session)
{
if(session.getAttribute("un")==null)
{
return "未登录";
}
else
{
return "张三家的大黑狗昨天下河游泳了";
}
}
}
以上,为Spring MVC下Session的使用。
拦截器鉴权
需求
在每个需要权限的方法里,都去写
if(session.getAttribute("un")==null)
{
return "未登录";
}
else
{
//业务代码
}
太麻烦了。
对于每个http请求进行权限鉴定,用拦截器拦截它们进行统一检查,是最合理的方式。
切片也可以实现。但拦截器更合适。两者在这里的区别可以自行搜索。
逻辑
-
自定义拦截器:
"Interceptor\LoginInterceptor.java" -
配置它的生效范围:
"Config\WebConfig.java"
达到目的:
-
登录接口
/login放行,不需要校验; -
其它接口,检查
HttpSession里有没有un; -
没有Session或Session 无登录信息 → 返回 JSON
401未登录; -
已登录 → 放行执行 Controller。
实现
LoginInterceptor.java:
package com.example.demo_web.Interceptor;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
@Component
public class LoginInterceptor implements HandlerInterceptor {
// controller方法执行之前执行
// return true:放行;return false:拦截,不进入controller
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
// 获取session,没有不新建
var session = request.getSession(false);
// 未登录
if(session==null || session.getAttribute("un") == null){
// 设置响应json,直接输出,不再走到Controller
response.setContentType("application/json;charset=utf-8");
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); //401
String json = "{\"code\":401,\"msg\":\"未登录,请先登录\"}";
response.getWriter().write(json);
return false; //拦截
}
//已登录,放行
return true;
}
//controller方法执行之后,返回内容之前执行
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView) {
}
//controller方法执行之后,返回内容之后执行
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) {
}
}
WebConfig.java:
package com.example.demo_web.Config;
import com.example.demo_web.Interceptor.LoginInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
private final LoginInterceptor loginInterceptor;
public WebConfig(LoginInterceptor loginInterceptor) {
this.loginInterceptor = loginInterceptor;
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginInterceptor)
.addPathPatterns("/**") //拦截所有请求
//放行登录接口,静态资源static下面html/js/css
.excludePathPatterns("/login")
.excludePathPatterns("/**/*.html","/**/*.js","/**/*.css");
}
}
最后一行的排除静态资源,实测不写也没影响。但各大教程都推荐写上,免除后患。
My_Controller.java可以简化成:
package com.example.demo_web.Controllers;
import java.util.Map;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import jakarta.servlet.http.HttpSession;
@RestController
public class My_Controller
{
@PostMapping("/login")
public String log_In(@RequestBody Map<String, String> body, HttpSession session)
{
String username = body.get("username");
if (username.length() > 4)
{
session.setAttribute("un", username);
return "OK";
}
else
{
if (session != null)
{
session.invalidate();
}
return "No";
}
}
@GetMapping("getstring")
public String getSth(HttpSession session)
{
return "张三家的大黑狗昨天下河游泳了";
}
}
浙公网安备 33010602011771号