万金流
以码会友。 吾Q:578751655。 水平有限,轻喷,谢!

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"

达到目的:

  1. 登录接口 /login 放行,不需要校验;

  2. 其它接口,检查 HttpSession 里有没有 un;

  3. 没有Session或Session 无登录信息 → 返回 JSON 401未登录;

  4. 已登录 → 放行执行 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 "张三家的大黑狗昨天下河游泳了";
    }
}
posted on 2026-10-10 11:21  万金流  阅读(48)  评论(0)    收藏  举报