实战篇04 05 06 1.用户输入的参数需要校验是否有效然后才能进数据库查询为了避免参数校验的if麻烦引入SpringBoo Validation起步依赖在要校验的参数前添加**P**attern使用正则表达式完成校验然后再controller类上添加Validated注解刷新Maven  运行一下postman测试  参数不符合接口文档让后显然现在的返回内容不符合接口文档因此还要修改使用全局异常处理器 定义一个类然后注释RestControllerAdvice注解-标识这个 类用于处理异常所有的方法返回值都会被返回成json字符串用方法来返回异常因此这个方法上要添加ExceptionHandlerException.class注解Exception.class表示所有异常方法返回类型是Result这样就可以保障返回类型符合接口文档要求。 创建包exception和controller同一个目录新建GlobalExceptionHandler类技巧return Result.error(StringUtils.hasLength(e.getMessage())?e.getMessage():“操作失败”);在写完StringUtils的时候如果直接altenter可能会调错包变成import com.mysql.cj.util.StringUtils;所以删掉它全部写完再试一次就会正确import org.springframework.util.StringUtils;RestControllerAdvicepublicclassGlobalExceptionHandler{ExceptionHandler(Exception.class)publicResulthandleException(Exceptione){//输出e方便调试e.printStackTrace();//e会封装错误信息,判断长度是否大于零returnResult.error(StringUtils.hasLength(e.getMessage())?e.getMessage():操作失败);}}2.登入根据用户查询用户在注册的时候已经写过了可以复用重点看Controller,注意MySQL返回的查询结果是根据名字返回查到的那一整行用的是select*Select(select * from user where username #{username})UserfindByUserName(Stringusername);Controller代码暂时忽略JWT令牌 javaPostMapping(/login)publicResultStringlogin(Pattern(regexp^\\S{5,16}$)Stringusername,Pattern(regexp^\\S{5,16}$)Stringpassword){//根据用户名查询用户UserloginUseruserService.findByUserName(username);//判断用户是否存在if(loginUsernull){returnResult.error(用户不存在);}//判断秘法是否正确if(Md5Util.getMD5String(password).equals(loginUser.getPassword())){returnResult.success(jwt token );}returnResult.error(密码错误);}3.JWT登入认证要登入以后才能访问相关的服务因此每个服务需要检查登录状态这个就是登录认证在Controller目录下新建ArticleController类RestControllerRequestMapping(/article)publicclassArticleController{GetMapping(/list)publicResultStringlist(){returnResult.success(所有的文章);}}执行后访问 http://localhost:8080/article/list登录认证原理生成一个令牌登录后检查令牌是否合法令牌的特点1.业务数据减少数据库请求次数如为记录请求来源就要查询用户信息为了避免则在令牌上加入用户信息2.防止篡改保障信息合法性和有效性4.JWTJson Web Token如是前后端之间传输的字符串通过 两个.把字符串分成三个部分第一个部分alg-加密算法type-令牌类型第二个部分有效载荷-业务数据如idusername基于Base64编码方式完成-基于64个可打印字符的二进制编码方式A-Za-z0-9/非加密且公开不要存放私密数据如密码第三部分数字签名将第一部分和第二部分加密后得到服务器拿到数据后将解密内容与前面比较引入JWT,刷新dependencygroupIdcom.auth0/groupIdartifactIdjava-jwt/artifactIdversion4.4.0/version/dependency 在test下新建一个类JwtTest作单元测试,熟悉一下jwt工具 java public class JwtTest { Test public void testGen() { MapString,Objectclaims new HashMap(); claims.put(id, 1); claims.put(name, 张三); //生成JWT String token JWT.create() .withClaim(user, claims)//添加载荷,claims是一个map集合 .withExpiresAt(new Date(System.currentTimeMillis() * 1000 * 60 * 60 * 12))//添加过期时间,毫秒时间-12h .sign(Algorithm.HMAC256(123));//指定算法设置加密密钥 System.out.println(token); } } Test //测试jwt工具密钥解析 public void testParse(){ //定义字符串上面的模拟用户传递jwt令牌 String token eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjp7Im5hbWUiOiLlvKDkuIkiLCJpZCI6IjEifSwiZXhwIjozMDcwMTMwNDk1MjkyOTkzfQ.8HQcqMpMNOnVihaZE9ldkN2tgGmSOwL-i0xr4Qypx3o;//需要重新从终端复制否则会因为超时验证失败 JWTVerifier jwtVerifier JWT.require(Algorithm.HMAC256(123)).build();//解密的密码 DecodedJWT decodedJWT jwtVerifier.verify(token);//生成一个解析后的JWT对象 MapString,Claimclaims decodedJWT.getClaims(); System.out.println(claims.get(user)); }4.JWT生成复制02资料\04_综合案例资料\02_后台资料\02_工具类\JWTUtil.java到工具类Utils目录下PostMapping(/login)publicResultStringlogin(Pattern(regexp^\\S{5,16}$)Stringusername,Pattern(regexp^\\S{5,16}$)Stringpassword){//根据用户名查询用户UserloginUseruserService.findByUserName(username);//判断用户是否存在if(loginUsernull){returnResult.error(用户不存在);}//判断秘法是否正确if(Md5Util.getMD5String(password).equals(loginUser.getPassword())){//登录成功-只修改这里MapString,ObjectclaimsnewHashMap();claims.put(id,loginUser.getId());claims.put(username,loginUser.getUsername());StringtokenJwtUtil.genToken(claims);returnResult.success(token);}returnResult.error(密码错误);}5.JWT验证浏览器与服务器通信的时候会携带token在header部分请求头位Authorization,值位登录时下发的令牌若未登录成功返回401如果收到的token解析失败则表示登录失败所以这里要使用异常处理技巧选中需要打包的代码然后点击包围ArticleControllerGetMapping(/list)publicResultStringlist(RequestHeader(nameAuthorization)Stringtoken,HttpServletResponseresponse){//验证token//如果该代码异常则失败否则成功try{MapString,ObjectclaimsJwtUtil.parseToken(token);}catch(Exceptione){//http响应状态码位401-使用HTTPServletResponseresponse.setStatus(401);returnResult.error(未登录);}returnResult.success(所有的文章);}未登录尝试:http://localhost:8080/article/list注意是GET方法,勾选请求头:6.拦截器当有多个接口都需要JWT认证的时候应该使用拦截器让所有接口直接调用还要实现一个配置类实现WebMvcConfiger把拦截器注册进去新建包interceptors\LoginInterceptor.java需要实现HandlerInterceptor接口添加Component注释ComponentpublicclassLoginInterceptorimplementsHandlerInterceptor{OverridepublicbooleanpreHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler)throwsException{//令牌验证//借助request对象拿到令牌-所有请求数据都在request对象中Stringtokenrequest.getHeader(Authorization);try{System.out.println(token);MapString,ObjectclaimsJwtUtil.parseToken(token);}catch(Exceptione){//http响应状态码位401-使用HTTPServletResponseresponse.setStatus(401);//不放行returnfalse;}//放行returntrue;}}新建文件夹config/WebConfig.java 实现接口WebMvcConfigererConfigurationpublicclassWebConfigimplementsWebMvcConfigurer{//在Interceptors中已经注入了一个LoginInterceptorAutowiredprivateLoginInterceptorloginInterceptor;OverridepublicvoidaddInterceptors(InterceptorRegistryregistry){//注入拦截器不过它会拦截所有接口包括注册和登录接口所以加入excludePathPatterns(),输入排除的接口路径registry.addInterceptor(loginInterceptor).addPathPatterns(/**).excludePathPatterns(/user/login,/user/register);}}注册不影响登录不影响获取文章列表影响打钩上Authorization,有JWT就可以正常访问1.RestControllerAdvice和ExceptionHandler(Exception.class)RestControllerAdvice自动拦截所有 Controller 或 RestController 中抛出的异常自动将返回值转换为 JSON因为包含了 ResponseBodyExceptionHandler(Exception.class)标记这个方法为异常处理器Exception.class表示处理所有类型的异常Exception 是所有异常的父类当Controller发生异常后会被捕获然后自动匹配ExceptionHandler中的异常调用对应的方法ExceptionHandlerRestControllerAdviceSpring 框架Controller客户端ExceptionHandlerRestControllerAdviceSpring 框架Controller客户端发送请求方法执行发生异常抛出异常捕获异常查找 RestControllerAdvice查找 ExceptionHandler匹配异常类型(Exception.class 匹配所有)调用 handleException(e)返回 Result 对象返回 ResultResult 转 JSON返回 JSON 响应冲突问题场景是否冲突行为不同异常类型同级✅ 不冲突各自处理自己的异常父子异常关系✅ 不冲突选择最精确的子类处理器同一个异常类同级❌ 冲突报错Ambiguous ExceptionHandler method mapped或随机选择多个处理器 Order✅ 不冲突按顺序执行第一个匹配的2.实体类Resut实体类Result中的方法都是静态方法所以return的时候不需要new一个新对象代码简洁,语义清晰如 return Result.error();所有错误响应格式一致设计模式 - 静态工厂方法方法名更有语义运行的时候缓存的是同一个对象不用new重新构造可以返回继承该类的子类型使用多个方法重载可以使得参数更灵活3.Exception e.printStackTrace();e.printStackTrace() 是 Java 中打印异常堆栈信息的方法用于调试和排查问题。4.Result是泛型类写法含义是否合法赋值给变量时类型强制转换?Result原始类型Raw Type✅ 合法但不推荐忽略泛型data 当 Object 用是Result泛型类型定义时用✅ 类型定义是定义时使用的不是使用时不存在强制类型转换问题Result参数化类型使用时用✅ 指定具体类型任何一个具体类都不用强制类型转换5.if(Md5Util.getMD5String(password).equals(loginUser.getPassword()))如果使用的话会报错因为比较的是内存地址比较数值的时候是比变量值比字符串常亮的时候也是比变量值比较的对象是像字符串这样的用于表示内存地址的符号的时候是比内存地址// 字面量写法直接写双引号Strings1hello;Strings2hello;System.out.println(s1s2);// true指向常量池中的同一个对象// new 关键字创建Strings3newString(hello);Strings4newString(hello);System.out.println(s3s4);// false堆中不同对象6.简洁性对于两个互斥的退出条件时采用if return的方法而不是if else因为更简洁if(Md5Util.getMD5String(password).equals(loginUser.getPassword())){returnResult.success(jwt token );}returnResult.error(密码错误);}7.jwt-混合了静态方法和实力方法的库// 你看的代码StringtokenJWT.create()// 1️⃣ 静态方法.withClaim(user,claims)// 2️⃣ 实例方法.withExpiresAt(expireDate)// 2️⃣ 实例方法.sign(Algorithm.HMAC256(123));// 2️⃣ 实例方法8.拦截器9.注释Component、Autowired、Configuration
黑马SpringBoot3+Vue3(实战篇)学习记录三:SpringBoot注册参数校验框架Validation、登入、JWT、拦截器、拦截器配置
发布时间:2026/5/19 7:16:21
实战篇04 05 06 1.用户输入的参数需要校验是否有效然后才能进数据库查询为了避免参数校验的if麻烦引入SpringBoo Validation起步依赖在要校验的参数前添加**P**attern使用正则表达式完成校验然后再controller类上添加Validated注解刷新Maven  运行一下postman测试  参数不符合接口文档让后显然现在的返回内容不符合接口文档因此还要修改使用全局异常处理器 定义一个类然后注释RestControllerAdvice注解-标识这个 类用于处理异常所有的方法返回值都会被返回成json字符串用方法来返回异常因此这个方法上要添加ExceptionHandlerException.class注解Exception.class表示所有异常方法返回类型是Result这样就可以保障返回类型符合接口文档要求。 创建包exception和controller同一个目录新建GlobalExceptionHandler类技巧return Result.error(StringUtils.hasLength(e.getMessage())?e.getMessage():“操作失败”);在写完StringUtils的时候如果直接altenter可能会调错包变成import com.mysql.cj.util.StringUtils;所以删掉它全部写完再试一次就会正确import org.springframework.util.StringUtils;RestControllerAdvicepublicclassGlobalExceptionHandler{ExceptionHandler(Exception.class)publicResulthandleException(Exceptione){//输出e方便调试e.printStackTrace();//e会封装错误信息,判断长度是否大于零returnResult.error(StringUtils.hasLength(e.getMessage())?e.getMessage():操作失败);}}2.登入根据用户查询用户在注册的时候已经写过了可以复用重点看Controller,注意MySQL返回的查询结果是根据名字返回查到的那一整行用的是select*Select(select * from user where username #{username})UserfindByUserName(Stringusername);Controller代码暂时忽略JWT令牌 javaPostMapping(/login)publicResultStringlogin(Pattern(regexp^\\S{5,16}$)Stringusername,Pattern(regexp^\\S{5,16}$)Stringpassword){//根据用户名查询用户UserloginUseruserService.findByUserName(username);//判断用户是否存在if(loginUsernull){returnResult.error(用户不存在);}//判断秘法是否正确if(Md5Util.getMD5String(password).equals(loginUser.getPassword())){returnResult.success(jwt token );}returnResult.error(密码错误);}3.JWT登入认证要登入以后才能访问相关的服务因此每个服务需要检查登录状态这个就是登录认证在Controller目录下新建ArticleController类RestControllerRequestMapping(/article)publicclassArticleController{GetMapping(/list)publicResultStringlist(){returnResult.success(所有的文章);}}执行后访问 http://localhost:8080/article/list登录认证原理生成一个令牌登录后检查令牌是否合法令牌的特点1.业务数据减少数据库请求次数如为记录请求来源就要查询用户信息为了避免则在令牌上加入用户信息2.防止篡改保障信息合法性和有效性4.JWTJson Web Token如是前后端之间传输的字符串通过 两个.把字符串分成三个部分第一个部分alg-加密算法type-令牌类型第二个部分有效载荷-业务数据如idusername基于Base64编码方式完成-基于64个可打印字符的二进制编码方式A-Za-z0-9/非加密且公开不要存放私密数据如密码第三部分数字签名将第一部分和第二部分加密后得到服务器拿到数据后将解密内容与前面比较引入JWT,刷新dependencygroupIdcom.auth0/groupIdartifactIdjava-jwt/artifactIdversion4.4.0/version/dependency 在test下新建一个类JwtTest作单元测试,熟悉一下jwt工具 java public class JwtTest { Test public void testGen() { MapString,Objectclaims new HashMap(); claims.put(id, 1); claims.put(name, 张三); //生成JWT String token JWT.create() .withClaim(user, claims)//添加载荷,claims是一个map集合 .withExpiresAt(new Date(System.currentTimeMillis() * 1000 * 60 * 60 * 12))//添加过期时间,毫秒时间-12h .sign(Algorithm.HMAC256(123));//指定算法设置加密密钥 System.out.println(token); } } Test //测试jwt工具密钥解析 public void testParse(){ //定义字符串上面的模拟用户传递jwt令牌 String token eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjp7Im5hbWUiOiLlvKDkuIkiLCJpZCI6IjEifSwiZXhwIjozMDcwMTMwNDk1MjkyOTkzfQ.8HQcqMpMNOnVihaZE9ldkN2tgGmSOwL-i0xr4Qypx3o;//需要重新从终端复制否则会因为超时验证失败 JWTVerifier jwtVerifier JWT.require(Algorithm.HMAC256(123)).build();//解密的密码 DecodedJWT decodedJWT jwtVerifier.verify(token);//生成一个解析后的JWT对象 MapString,Claimclaims decodedJWT.getClaims(); System.out.println(claims.get(user)); }4.JWT生成复制02资料\04_综合案例资料\02_后台资料\02_工具类\JWTUtil.java到工具类Utils目录下PostMapping(/login)publicResultStringlogin(Pattern(regexp^\\S{5,16}$)Stringusername,Pattern(regexp^\\S{5,16}$)Stringpassword){//根据用户名查询用户UserloginUseruserService.findByUserName(username);//判断用户是否存在if(loginUsernull){returnResult.error(用户不存在);}//判断秘法是否正确if(Md5Util.getMD5String(password).equals(loginUser.getPassword())){//登录成功-只修改这里MapString,ObjectclaimsnewHashMap();claims.put(id,loginUser.getId());claims.put(username,loginUser.getUsername());StringtokenJwtUtil.genToken(claims);returnResult.success(token);}returnResult.error(密码错误);}5.JWT验证浏览器与服务器通信的时候会携带token在header部分请求头位Authorization,值位登录时下发的令牌若未登录成功返回401如果收到的token解析失败则表示登录失败所以这里要使用异常处理技巧选中需要打包的代码然后点击包围ArticleControllerGetMapping(/list)publicResultStringlist(RequestHeader(nameAuthorization)Stringtoken,HttpServletResponseresponse){//验证token//如果该代码异常则失败否则成功try{MapString,ObjectclaimsJwtUtil.parseToken(token);}catch(Exceptione){//http响应状态码位401-使用HTTPServletResponseresponse.setStatus(401);returnResult.error(未登录);}returnResult.success(所有的文章);}未登录尝试:http://localhost:8080/article/list注意是GET方法,勾选请求头:6.拦截器当有多个接口都需要JWT认证的时候应该使用拦截器让所有接口直接调用还要实现一个配置类实现WebMvcConfiger把拦截器注册进去新建包interceptors\LoginInterceptor.java需要实现HandlerInterceptor接口添加Component注释ComponentpublicclassLoginInterceptorimplementsHandlerInterceptor{OverridepublicbooleanpreHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler)throwsException{//令牌验证//借助request对象拿到令牌-所有请求数据都在request对象中Stringtokenrequest.getHeader(Authorization);try{System.out.println(token);MapString,ObjectclaimsJwtUtil.parseToken(token);}catch(Exceptione){//http响应状态码位401-使用HTTPServletResponseresponse.setStatus(401);//不放行returnfalse;}//放行returntrue;}}新建文件夹config/WebConfig.java 实现接口WebMvcConfigererConfigurationpublicclassWebConfigimplementsWebMvcConfigurer{//在Interceptors中已经注入了一个LoginInterceptorAutowiredprivateLoginInterceptorloginInterceptor;OverridepublicvoidaddInterceptors(InterceptorRegistryregistry){//注入拦截器不过它会拦截所有接口包括注册和登录接口所以加入excludePathPatterns(),输入排除的接口路径registry.addInterceptor(loginInterceptor).addPathPatterns(/**).excludePathPatterns(/user/login,/user/register);}}注册不影响登录不影响获取文章列表影响打钩上Authorization,有JWT就可以正常访问1.RestControllerAdvice和ExceptionHandler(Exception.class)RestControllerAdvice自动拦截所有 Controller 或 RestController 中抛出的异常自动将返回值转换为 JSON因为包含了 ResponseBodyExceptionHandler(Exception.class)标记这个方法为异常处理器Exception.class表示处理所有类型的异常Exception 是所有异常的父类当Controller发生异常后会被捕获然后自动匹配ExceptionHandler中的异常调用对应的方法ExceptionHandlerRestControllerAdviceSpring 框架Controller客户端ExceptionHandlerRestControllerAdviceSpring 框架Controller客户端发送请求方法执行发生异常抛出异常捕获异常查找 RestControllerAdvice查找 ExceptionHandler匹配异常类型(Exception.class 匹配所有)调用 handleException(e)返回 Result 对象返回 ResultResult 转 JSON返回 JSON 响应冲突问题场景是否冲突行为不同异常类型同级✅ 不冲突各自处理自己的异常父子异常关系✅ 不冲突选择最精确的子类处理器同一个异常类同级❌ 冲突报错Ambiguous ExceptionHandler method mapped或随机选择多个处理器 Order✅ 不冲突按顺序执行第一个匹配的2.实体类Resut实体类Result中的方法都是静态方法所以return的时候不需要new一个新对象代码简洁,语义清晰如 return Result.error();所有错误响应格式一致设计模式 - 静态工厂方法方法名更有语义运行的时候缓存的是同一个对象不用new重新构造可以返回继承该类的子类型使用多个方法重载可以使得参数更灵活3.Exception e.printStackTrace();e.printStackTrace() 是 Java 中打印异常堆栈信息的方法用于调试和排查问题。4.Result是泛型类写法含义是否合法赋值给变量时类型强制转换?Result原始类型Raw Type✅ 合法但不推荐忽略泛型data 当 Object 用是Result泛型类型定义时用✅ 类型定义是定义时使用的不是使用时不存在强制类型转换问题Result参数化类型使用时用✅ 指定具体类型任何一个具体类都不用强制类型转换5.if(Md5Util.getMD5String(password).equals(loginUser.getPassword()))如果使用的话会报错因为比较的是内存地址比较数值的时候是比变量值比字符串常亮的时候也是比变量值比较的对象是像字符串这样的用于表示内存地址的符号的时候是比内存地址// 字面量写法直接写双引号Strings1hello;Strings2hello;System.out.println(s1s2);// true指向常量池中的同一个对象// new 关键字创建Strings3newString(hello);Strings4newString(hello);System.out.println(s3s4);// false堆中不同对象6.简洁性对于两个互斥的退出条件时采用if return的方法而不是if else因为更简洁if(Md5Util.getMD5String(password).equals(loginUser.getPassword())){returnResult.success(jwt token );}returnResult.error(密码错误);}7.jwt-混合了静态方法和实力方法的库// 你看的代码StringtokenJWT.create()// 1️⃣ 静态方法.withClaim(user,claims)// 2️⃣ 实例方法.withExpiresAt(expireDate)// 2️⃣ 实例方法.sign(Algorithm.HMAC256(123));// 2️⃣ 实例方法8.拦截器9.注释Component、Autowired、Configuration