SpringBoot 创建及登录、拦截器的实现
<div id="navCategory"><h5 class="catalogue">目录</h5><ul class="first_class_ul"><li>1.创建</li><li>2.登录和拦截器</li><ul class="second_class_ul"><li>2.1 session</li><ul class="third_class_ul"><li>2.1.1 登录</li></ul><li>2.2 token</li><ul class="third_class_ul"><li>token 说明</li><li>session 和 token 的区别</li><li>2.2.1 常建工具类用于创建token和解析token密钥</li><li>2.2.2 修改登录</li></ul></ul><li>3.springboot统一的响应格式</li><ul class="second_class_ul"><li>springboot统一的响应格式好处:</li><ul class="third_class_ul"><li>标准化接口设计</li><li>错误处理规范化</li><li>简化客户端处理</li><li>增强可维护性</li><li>监控和统计</li><li>实现方式</li><li>典型应用场景</li></ul><li>具体实现:</li><ul class="third_class_ul"></ul></ul></ul></div><p class="maodian"></p><h2>1.创建</h2><p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202601/2026011214370540.png" /></p>
<p>这里jdk1.8推荐使用java21</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202601/2026011214370526.png" /></p>
<p><strong>pom文件:</strong></p>
<div class="jb51code"><pre class="brush:xml;"><?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.6</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.qcby</groupId>
<artifactId>SpringBootDemoTest</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>SpringBootDemoTest</name>
<description>SpringBootDemoTest</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--加载web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--加载Lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--加载mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope> runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project></pre></div>
<p class="maodian"></p><h2>2.登录和拦截器</h2>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202601/2026011214370552.png" /></p>
<p>HTTP协议本身是无状态的,这意味着服务器不会保存任何与客户端交互的状态信息。每个HTTP请求都是独立的,服务器在处理完当前请求后不会记住之前的请求信息。这种设计是HTTP协议的核心特性之一。</p>
<p>这种无状态特性带来以下具体表现:</p>
<ol><li>每个请求必须包含服务器处理该请求所需的全部信息</li><li>服务器不会基于之前的请求来推断当前请求的上下文</li><li>默认情况下,服务器不会记录客户端的历史访问记录</li></ol>
<p>无状态设计的优缺点: 优点:</p>
<ul><li>简化服务器设计,降低服务器资源消耗</li><li>提高可扩展性,服务器可以更容易地处理大量并发请求</li><li>减少服务器故障时的数据丢失风险</li></ul>
<p>缺点:</p>
<ul><li>无法直接支持需要保持状态的Web应用(如购物车、用户登录等)</li><li>需要额外机制(如Cookie、Session)来实现状态管理</li></ul>
<p>为了解决无状态带来的限制,Web开发中常采用以下技术:</p>
<ol><li>Cookie:在客户端存储少量数据</li><li>Session:在服务器端保存用户状态</li><li>Token:通过令牌验证用户身份</li></ol>
<p>典型应用场景对比:</p>
<ul><li>静态网页:无状态完全适用</li><li>动态网页:需要额外状态管理机制</li><li>Web应用:必须实现某种形式的状态保持</li></ul>
<p class="maodian"></p><h3>2.1 session</h3>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202601/2026011214370565.png" /></p>
<p>Session(会话)是Web开发中用于跟踪用户状态的重要机制。以下是关于session的详细说明:</p>
<ol><li>基本概念</li></ol>
<ul><li>服务器端存储的用户状态信息</li><li>每个用户拥有独立的session数据</li><li>通常通过cookie中的session ID来识别用户</li></ul>
<ol><li>工作原理</li></ol>
<ol><li>客户端首次访问时,服务器创建session</li><li>服务器生成唯一session ID并返回给客户端</li><li>客户端后续请求携带session ID</li><li>服务器根据ID查找对应的session数据</li></ol>
<ol><li>典型应用场景</li></ol>
<ul><li>用户登录状态维护</li><li>购物车功能实现</li><li>个性化设置存储</li><li>表单数据跨页面暂存</li></ul>
<p class="maodian"></p><h4>2.1.1 登录</h4>
<div class="jb51code"><pre class="brush:java;">@GetMapping("/login")
@ResponseBody
public String login(Users users, HttpServletRequest request) {
List<Users> list = usersDao.login(users);
if(list.size()==1){
request.getSession().setAttribute("users", list.get(0));//保存用户信息
return "login success";
}else {
return "login fail";
}
}</pre></div>
<p>jsessionid每次登录都不一样</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202601/2026011214370521.png" /></p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202601/2026011214370583.png" /></p>
<p>2.1.2 session拦截器</p>
<div class="jb51code"><pre class="brush:java;">package com.qcby.springbootdemotest.interceptor;
import com.qcby.springbootdemotest.model.Users;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
登录拦截器
HandlerInterceptor 该接口是springmvc提供的一个接口,拦截器的主要接口*/@Componentpublic class LoginInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Users users = (Users) request.getSession().getAttribute("users");// 从session中获取用户信息
if(users != null){
return true;// true 表示放行,false 表示不放行
}else {
response.getWriter().append("please login");
return false;
}
}
}</pre></div>
<p>添加拦截器:</p>
<div class="jb51code"><pre class="brush:java;">package com.qcby.springbootdemotest.config;
import com.qcby.springbootdemotest.interceptor.LoginInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
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 LoginConfig implements WebMvcConfigurer {
@Autowired
private LoginInterceptor loginInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginInterceptor)//添加拦截器
.addPathPatterns("/**") //拦截所有请求
.excludePathPatterns("/users/login");//放行接口
}
}</pre></div>
<p class="maodian"></p><h3>2.2 token</h3>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202601/2026011214370531.png" /></p>
<p class="maodian"></p><h4>token 说明</h4>
<p>token 是服务端生成的一串字符串,目的是作为客户端进行请求的一个令牌。当第一次登录后,服务器生成一个 token (一串字符串),并将此 token 返回给客户端,此后页面接收到请求后,只需要找到 token 即可获取信息,无需再输入登录名和密码。</p>
<p class="maodian"></p><h4>session 和 token 的区别</h4>
<p>session 和 token 的区别:但 session 有一个缺陷:如果 web 服务器做了负载均衡,那么下一个操作请求到了另一台服务器的时候 session 会丢失。而 token 最大特点就是支持跨平台操作,不论是在 App 还是在 PC 端,token 都可以保留。</p>
<p class="maodian"></p><h4>2.2.1 常建工具类用于创建token和解析token密钥</h4>
<div class="jb51code"><pre class="brush:java;">package com.qcby.springbootdemotest.util;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
import java.util.Date;
import java.util.UUID;
/**
JWT工具类/public class JwtUtil {//有效期为public static final long JWT_TTL = 6060*1000L;//60 * 60 *1000 一个小时//设置密钥明文public static final String JWT_KEY = "qcby";
/**
生成jwt
@param id
@param subject
@param ttlMillis
@return*/public static String createJWT(String id, String subject, Long ttlMillis){SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;long nowMillis = System.currentTimeMillis();Date now = new Date(nowMillis);if(ttlMillis==null){ttlMillis = JwtUtil.JWT_TTL;}long expMillis = nowMillis + ttlMillis;Date expDate = new Date(expMillis);SecretKey secretKey = generalKey();
JwtBuilder builder = Jwts.builder()
.setId(id) // jwt的唯一标识,根据业务需要,可以设置为一个不重复的值
.setSubject(subject)// 主题 可以是JSON数据
.setIssuer("wd") // 签发者
.setIssuedAt(now)// 签发时间
.signWith(signatureAlgorithm, secretKey) //使用HS256签名算法和密钥
.setExpiration(expDate); // 设置过期时间
return builder.compact();
}
/**
生成加密后的密钥 secretKey
@return*/public static SecretKey generalKey(){byte[] encodedKey = Base64.getDecoder().decode(JwtUtil.JWT_KEY);SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");return key;}
/**
解密jwt
@param jwt
@return
@throws Exception*/public static Claims parseJWT(String jwt) throws Exception{SecretKey secretKey = generalKey();return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(jwt).getBody();}
public static void main(String[] args) {
String token = JwtUtil.createJWT(UUID.randomUUID().toString(),"qd", null);
System.out.println(token);
}
}</pre></div>
<p>导入依赖</p>
<div class="jb51code"><pre class="brush:plain;"><dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency></pre></div>
<p class="maodian"></p><h4>2.2.2 修改登录</h4>
<div class="jb51code"><pre class="brush:java;">@GetMapping("/login")
@ResponseBody
public String login(Users users, HttpServletRequest request) {
List<Users> list = usersDao.login(users);
if(list.size()==1){
String token = JwtUtil.createJWT(list.get(0).getId().toString(), list.get(0).getUsername(), null);
return token;
}else {
return "login fail";
}
}</pre></div>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202601/2026011214370585.png" /></p>
<p>token优化:</p>
<div class="jb51code"><pre class="brush:java;">@PostMapping("/login")
@ResponseBody
public ResponseResult login(@RequestBody Users users, HttpServletRequest request) {
List<Users> list = usersDao.login(users);
Map<String,Object> map;
if(list.size()==1) {
map = new HashMap<>();
//生成token
String token = JwtUtil.createJWT(list.get(0).getId().toString(), list.get(0).getUsername(), null);
map.put("token", token);
return new ResponseResult(200,"success",map);
}
return new ResponseResult(400,"fail");
}</pre></div>
<p>拦截器</p>
<div class="jb51code"><pre class="brush:java;">package com.qcby.springbootdemotest.interceptor;
import com.qcby.springbootdemotest.model.Users;
import com.qcby.springbootdemotest.util.JwtUtil;
import io.jsonwebtoken.Claims;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 登录拦截器
* HandlerInterceptor 该接口是springmvc提供的一个接口,拦截器的主要接口
*/
@Component
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token = request.getHeader("token");//获取请求头中的token
if(!StringUtils.hasText(token)){//token为空
throw new RuntimeException("请登录");
}
//解析 token
try{
Claims claims = JwtUtil.parseJWT(token);//解析token
}catch (Exception e){
e.printStackTrace();
response.sendError(401);//响应错误
throw new RuntimeException("请登录");
}
return true;
}
}</pre></div>
<p>指定拦截异常类</p>
<div class="jb51code"><pre class="brush:java;">package com.qcby.springbootdemotest.expection;
import com.qcby.springbootdemotest.model.ResponseResult;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
@ControllerAdvice
public class MyControllerAdvice {
@ResponseBody
@ExceptionHandler(RuntimeException.class)//指定拦截的异常
public ResponseResult handleException(Exception e){
String message = e.getMessage();//获取异常信息
ResponseResult result = new ResponseResult(400,message);
return result;
}
}</pre></div>
<p class="maodian"></p><h2>3.springboot统一的响应格式</h2>
<p class="maodian"></p><h3>springboot统一的响应格式好处:</h3>
<p class="maodian"></p><h4>标准化接口设计</h4>
<ul><li>统一的数据结构使前后端协作更高效,前端开发者可以预期固定的响应格式</li><li>示例:所有接口返回<code>{"code":200,"message":"success","data":{...}}</code>格式</li><li>避免了不同开发者各自定义响应格式导致的混乱</li></ul>
<p class="maodian"></p><h4>错误处理规范化</h4>
<ul><li>统一错误码和错误信息格式,便于客户端识别和处理异常</li><li>可预定义常见的HTTP状态码(200,400,401,403,404,500等)</li><li>示例:<code>{"code":401,"message":"未授权访问","data":null}</code></li></ul>
<p class="maodian"></p><h4>简化客户端处理</h4>
<ul><li>前端无需为不同接口编写不同的响应解析逻辑</li><li>移动端应用可以复用统一的响应解析模块</li><li>示例:可以封装通用的响应拦截器处理所有API响应</li></ul>
<p class="maodian"></p><h4>增强可维护性</h4>
<ul><li>统一修改响应格式时只需修改一处代码</li><li>方便添加全局的响应拦截和日志记录</li><li>便于实现响应数据的统一加密或签名</li></ul>
<p class="maodian"></p><h4>监控和统计</h4>
<ul><li>统一的格式便于收集和分析接口调用情况</li><li>可以基于响应码统计系统健康状态</li><li>示例:监控系统可以统计不同错误码的出现频率</li></ul>
<p class="maodian"></p><h4>实现方式</h4>
<ol><li>使用<code>@RestControllerAdvice</code>创建全局响应处理器</li><li>自定义<code>ResponseEntity</code>封装类</li><li>统一处理异常转换为标准格式</li><li>可结合AOP实现响应数据的增强处理</li></ol>
<p class="maodian"></p><h4>典型应用场景</h4>
<ul><li>微服务架构中需要规范化的API响应</li><li>前后端分离项目中需要明确的数据契约</li><li>需要提供开放API的第三方服务</li><li><p><span>需要严格监控接口质量的系统</span></p></li></ul>
<p class="maodian"></p><h3>具体实现:</h3>
<div class="jb51code"><pre class="brush:java;">@RequestMapping("/findAll")
@ResponseBody
public ResponseResult findAll() {
List<Users> list = usersDao.findAll();
return new ResponseResult<>(200,list);
}
/**
* 根据用户名查询用户
* @param username
* @return
* @PathVariable 路径变量, 路径参数
*/
@RequestMapping("/findByUsername")
@ResponseBody
public ResponseResult findByUsername(String username) {
List<Users> user = usersDao.findByName(username);
return new ResponseResult<>(200,"suc",user);
}
@PostMapping("/insert")
@ResponseBody
public ResponseResult insert(@RequestBody Users users) {
int count = usersDao.insert(users);
if(count>0){
return new ResponseResult(200,"suc");
}else {
return new ResponseResult(400,"fail");
}
}
@PostMapping("/update")
@ResponseBody
public ResponseResult update(@RequestBody Users users) {
int count = usersDao.update(users);
if (count>0){
return new ResponseResult(200,"suc");
}else {
return new ResponseResult(400,"fail");
}
}
@PostMapping("/delete/{id}")
@ResponseBody
public ResponseResult delete(@PathVariable Integer id) {
int count = usersDao.delete(id);
if (count>0){
return new ResponseResult(200,"suc");
}else {
return new ResponseResult(400,"fail");
}
}</pre></div>
<p>ResponseResult类</p>
<div class="jb51code"><pre class="brush:java;">package com.qcby.springbootdemotest.model;
import lombok.Data;
import lombok.ToString;
@Data
@ToString
public class ResponseResult<T> {
private Integer code;//状态码
private String message;//提示信息
private T data;// 数据
public ResponseResult(Integer code, String message, T data) {
this.code = code;
this.message = message;
this.data = data;
}
public ResponseResult(Integer code, String message) {
this.code = code;
this.message = message;
}
public ResponseResult(Integer code, T data) {
this.code = code;
this.data = data;
}
}</pre></div>
<p>到此这篇关于SpringBoot 创建及登录、拦截器的实现的文章就介绍到这了,更多相关SpringBoot 创建登录拦截器内容请搜索琼殿技术社区以前的文章或继续浏览下面的相关文章希望大家以后多多支持琼殿技术社区!</p>
<div class="art_xg">
<b>您可能感兴趣的文章:</b><ul><li>IDEA下创建SpringBoot+MyBatis+MySql项目实现动态登录与注册功能</li><li>SpringBoot创建JSP登录页面功能实例代码</li><li>SpringBoot中使用拦截器拦截跳转登录的两种实现方法</li><li>springboot登录拦截器+ThreadLocal实现用户信息存储的实例代码</li><li>SpringBoot中实现登录拦截器的代码实例</li><li>SpringBoot实现登录拦截器超详细教程分享</li><li>SpringBoot实现登录拦截器的方法详解</li><li>SpringBoot拦截器实现登录拦截的方法示例</li></ul>
</div>
</div>
<!--endmain--> 看到楼主分享了这么详细的SpringBoot登录和拦截器教程,真的很用心!点赞
之前我自己搭建SpringBoot项目的时候,在登录这块折腾了很久,看完你的帖子感觉清晰多了。特别是session和token的区别讲解得很清楚,之前一直疑惑为什么有时候换台服务器登录状态就失效了,现在终于明白了。
想请教几个小问题:
1. 关于Token这块,你用的是jjwt 0.9.0版本,这个版本好像比较老了,最新版本有些API有变化,不知道楼主有没有踩过坑?
2. 关于拦截器配置,我之前遇到过静态资源被拦截的问题,请问你们项目中是怎么处理静态资源的?需要额外排除那些路径吗?
3. 统一响应格式那块,你们在实际项目中有没有考虑过分页的情况?一般分页数据会怎么封装在ResponseResult里?
另外补充一点小建议:
[*]如果项目要部署到多台服务器,token方案确实比session更合适
[*]生产环境中,JWT_KEY建议放到配置文件或者使用环境变量,不要硬编码
[*]拦截器里获取token的请求头名称最好也做成可配置的
希望以后能看到更多SpringBoot相关的实战教程!支持楼主!
頁:
[1]