每日总结38
SpringBoot学习6(2.2整合项目:properties->yml,@Value->@Configuration,登录校验:会话(令牌Jwt)+统一拦截(Filter、Interceptor),异常处理)
1.代码优化
1.1properties配置文件优化为yml配置文件
这里的这几个参数都是写在java文件中,不灵活,修改的时候需要自己找到该java文件才能修改,可以将这些参数写在配置文件application.properties中,然后再使用注解@Value进行外部属性的注入
修改:
将properties文件换成yml文件,语法更加简洁
application.yml
spring: #数据库连接信息 datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/course username: root password: 123456 servlet: multipart: max-file-size: 10MB max-request-size: 100MB mybatis: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 开启驼峰映射 map-underscore-to-camel-case: true mybatis-plus: global-config: db-config: id-type: auto # 自定义阿里云OSS配置 aliyun: oss: endpoint : https://oss-cn-beijing.aliyuncs.com accessKeyId : L accessKeySecret : F bucketName : web-tlias29
2.导入ConfigurationProperties依赖优化@Values注释获取配置文件的自定义数据
@Value注解优化@ConfigurationProperties
@ConfigurationProperties可以批量的将外部属性配置注入到bean对象的属性中
1. 引入依赖
2. 创建一个实体类
package com.example.utils; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; @Data @Component @ConfigurationProperties(prefix = "aliyun.oss") public class AliOSSProperties { private String endpoint; private String accessKeyId; private String accessKeySecret; private String bucketName; }
3.再调用该实体类
2.登录校验
2.1 会话技术
会话:用户打开浏览器访问web服务端的资源,会话建立,直到有一方断开连接,会话结束。
会话可以包含多次请求和响应。
会话跟踪:一种维护浏览器状态的方法,服务器需要识别多次请求是否来自同一浏览器,以便在同一次会话的多次请求间共享数据。
会话跟踪方案:
客户端会话跟踪技术:Cookie
服务端会话跟踪技术:Session
令牌技术(当前主流)
1.Cookie和Session运用
package com.example.controller; import com.example.entity.Result; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; @Slf4j @RestController public class SessionController { /** * 设置cookie * @param response * @return */ @GetMapping("/c1") public Result cookie1(HttpServletResponse response){ //设置 Cookie响应Cookie response.addCookie(new Cookie("login_username","admin")); return Result.success(); } /** * 获取cookie */ @GetMapping("/c2") public Result cookie2(HttpServletRequest request){ Cookie[] cookies = request.getCookies(); for (Cookie cookie:cookies) { if(cookie.getName().equals("login_username")){ System.out.println("login_username: "+cookie.getValue()); } } return Result.success(); } /** * 设置Session */ @GetMapping("/s1") public Result session1(HttpSession session){ log.info("HttpSession-s1:{}",session.hashCode()); session.setAttribute("loginUser","tom"); return Result.success(); } /** * 获取Session的值 */ @GetMapping("/s2") public Result session2(HttpServletRequest request){ HttpSession session = request.getSession(); log.info("HttpSession-s2:{}",session.hashCode()); Object loginUser = session.getAttribute("loginUser"); log.info("loginUser:{}",loginUser); return Result.success(); } }
2.令牌技术JWT(JSON Web Token)
定义了一种简洁的、自包含的格式,用于在通信双方以json数据格式安全的传输信息,由于数字签名的存在,这些信息是可靠的。
组成:
第一部分:Header(头),记录令牌的类型、签名算法
第二部分:Payload(有效载荷),携带自定义信息、默认信息
第三部分:Signature(签名),防止Token被篡改,确保安全。
①pom.xml引入依赖
<!-- JWT令牌 --> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency>
②测试使用代码
测试运行的时候,这个代码和SpringBoot无关,可以先把@SpringBootTest注释掉就不用加载整个SpringBoot项目,运行testGenJwt
package com.example; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.UUID; @SpringBootTest public class JJWTTest { @Test public void testUuid(){ for(int i=0;i<1000;i++){ String uuid = UUID.randomUUID().toString(); System.out.println(uuid); } } /** * 测试生成JWt令牌 */ @Test public void testGenJwt(){ Map<String,Object> claims = new HashMap<>(); claims.put("id",1); claims.put("name","xiaoming"); String jwt = Jwts.builder() //设置签名算法,密钥 .signWith(SignatureAlgorithm.HS256, "1010") //设置自定义数据 .setClaims(claims) //设置令牌的有效期,newDate是当前时间, // System.currentTimeMillis()是获取当前的时间毫秒值然后 //+3600*1000表示当前时间+1h(有效期) .setExpiration(new Date(System.currentTimeMillis() + 3600 * 1000)) //获取返回值就是jwt令牌 .compact(); System.out.println(jwt); } }
运行testGenJwt的结果:
将结果复制到jwt官网查看:
③解析令牌的java代码
@Test public void testParseJwt(){ Claims claims = Jwts.parser() //给出密钥,才能解析该令牌 .setSigningKey("admin") //将令牌传进去 .parseClaimsJws("eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoieGlhb21pbmciLCJpZCI6MSwiZXhwIjoxNjk3NDYyMTUyfQ.5CURAl3in17KoZZnbvOiZJdjqWZ8vrAsGPHr6P4uVt8") //拿到令牌第二部分的内容 .getBody(); System.out.println(claims); }
④结合项目
JwtUtils.java
package com.example.utils; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import java.util.Date; import java.util.Map; public class JwtUtils { private static String signKey = "admin"; private static Long expire = 43200000L; /** * 生成JWT令牌 * @param claims JWT第二部分负载 payload 中存储的内容 * @return */ public static String generateJwt(Map<String, Object> claims){ String jwt = Jwts.builder() .addClaims(claims) .signWith(SignatureAlgorithm.HS256, signKey) .setExpiration(new Date(System.currentTimeMillis() + expire)) .compact(); return jwt; } /** * 解析JWT令牌 * @param jwt JWT令牌 * @return JWT第二部分负载 payload 中存储的内容 */ public static Claims parseJWT(String jwt){ Claims claims = Jwts.parser() .setSigningKey(signKey) .parseClaimsJws(jwt) .getBody(); return claims; } }
LoginController.java
package com.example.controller; import com.example.entity.Emp; import com.example.entity.Result; import com.example.service.EmpService; import com.example.utils.JwtUtils; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.HashMap; import java.util.Map; @Slf4j @RestController @RequestMapping("/login") public class LoginController { @Autowired private EmpService empService; @PostMapping() public Result login(@RequestBody Emp emp){ log.info("员工登录{}",emp); Emp e = empService.login(emp); //登录成功,生成令牌 if(e!=null){ Map<String, Object> claims= new HashMap<>(); claims.put("id",e.getId()); claims.put("name",e.getName()); claims.put("username",e.getUsername()); String jwt = JwtUtils.generateJwt(claims); //将令牌返回到前端 return Result.success(jwt); } //登录失败,返回错误信息 return Result.error("用户名或密码错误"); } }
在服务层、数据层添加查询代码:
//EmpService Emp login(Emp emp); //EmpServiceImpl @Override public Emp login(Emp emp) { return empMapper.getByUsernameAndPassword(emp); } //EmpMapper @Select("select * from emp where username = #{username} and password = #{password}") Emp getByUsernameAndPassword(Emp emp);
2.2 过滤器Filter和拦截器Interceptor
1.过滤器Filter
Filter过滤器是JavaWeb三大组件(Servlet、Filter、Listener)之一。
可以将对资源的请求拦截下来,常用于登录校验、统一编码处理、敏感字符处理
步骤:
①定义一个类实现Filter接口,重写其所有方法
②配置Filter,在Filter类中加上@WebFilter(urlPatterns="/*)注解,配置拦截资源的路径,引导类中 (在项目启动的xxxApplication) 中加入注解@ServletComponentScan .
①结合项目
添加依赖
<!-- 阿里巴巴提供的fastjson工具包--> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.62</version> </dependency>
LoginCheckFilter.java
package com.example.filter; import com.alibaba.fastjson.JSONObject; import com.example.entity.Result; import com.example.utils.JwtUtils; import lombok.extern.slf4j.Slf4j; import org.springframework.util.StringUtils; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @Slf4j @WebFilter(urlPatterns = "/*") public class LoginCheckFilter implements Filter { @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { //拿到请求对象和响应对象 HttpServletRequest req = (HttpServletRequest) servletRequest; HttpServletResponse resp = (HttpServletResponse) servletResponse; //获取请求的url String url = req.getRequestURL().toString(); log.info("请求的url:{}",url); //判断url中有没有包含login,有则放行 if(url.contains("login")){ log.info("登录操作,放行"); filterChain.doFilter(servletRequest,servletResponse); return; } //不是登录操作,获取令牌的请求头的token String jwt = req.getHeader("token"); //判断令牌是否存在,如果不存在返回错误结果(未登录) if(!StringUtils.hasLength(jwt)){ //没有登录的信息 log.info("请求头token为空,即未登录"); Result error=Result.error("NOT_LOGIN"); //手动将对象转为JSON返回 引入阿里巴巴提供的fastjson工具包 String notloing = JSONObject.toJSONString(error); //响应数据 resp.getWriter().write(notloing); return; } //令牌存在,解析jwt令牌是否解析成功(有无登录失效、令牌篡改) try { JwtUtils.parseJWT(jwt); }catch (Exception e){ e.printStackTrace(); log.info("解析令牌失败"); Result error=Result.error("NOT_LOGIN"); //手动将对象转为JSON返回 引入阿里巴巴提供的fastjson工具包 String notloing = JSONObject.toJSONString(error); //响应数据 resp.getWriter().write(notloing); return; } //放行 log.info("令牌合法,放行"); filterChain.doFilter(servletRequest,servletResponse); } }
JwtUtils.java
package com.example.utils; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import java.util.Date; import java.util.Map; public class JwtUtils { private static String signKey = "admin"; private static Long expire = 43200000L; /** * 生成JWT令牌 * @param claims JWT第二部分负载 payload 中存储的内容 * @return */ public static String generateJwt(Map<String, Object> claims){ String jwt = Jwts.builder() .addClaims(claims) .signWith(SignatureAlgorithm.HS256, signKey) .setExpiration(new Date(System.currentTimeMillis() + expire)) .compact(); return jwt; } /** * 解析JWT令牌 * @param jwt JWT令牌 * @return JWT第二部分负载 payload 中存储的内容 */ public static Claims parseJWT(String jwt){ Claims claims = Jwts.parser() .setSigningKey(signKey) .parseClaimsJws(jwt) .getBody(); return claims; } }
运行:
2.拦截器Interceptor
一种动态拦截方法的调用机制类似于过滤器,Spring框架中天空,用于动态拦截控制器方法的执行。
和拦截器的代码差不多,放行的代码不一样,放行直接写return true即可。
代码:
LoginCheckInterceptor.java
package com.example.interceptor; import com.alibaba.fastjson.JSONObject; import com.example.entity.Result; import com.example.utils.JwtUtils; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @Slf4j @Component public class LoginCheckInterceptor implements HandlerInterceptor { @Override //目标资源方法运行前运行, 返回true: 放行, 放回false, 不放行 public boolean preHandle(HttpServletRequest req, HttpServletResponse resp, Object handler) throws Exception { // return HandlerInterceptor.super.preHandle(request, response, handler); // System.out.println("preHandle ..."); //登录校验:和过滤器的代码一样 //获取请求的url String url = req.getRequestURL().toString(); log.info("请求的url:{}",url); //判断url中有没有包含login,有则放行 if(url.contains("login")){ log.info("登录操作,放行"); return true;//放行 } //不是登录操作,获取令牌的请求头的token String jwt = req.getHeader("token"); //判断令牌是否存在,如果不存在返回错误结果(未登录) if(!StringUtils.hasLength(jwt)){ //没有登录的信息 log.info("请求头token为空,即未登录"); Result error=Result.error("NOT_LOGIN"); //手动将对象转为JSON返回 引入阿里巴巴提供的fastjson工具包 String notloing = JSONObject.toJSONString(error); //响应数据 resp.getWriter().write(notloing); return false; } //令牌存在,解析jwt令牌是否解析成功(有无登录失效、令牌篡改) try { JwtUtils.parseJWT(jwt); }catch (Exception e){ e.printStackTrace(); log.info("解析令牌失败"); Result error=Result.error("NOT_LOGIN"); //手动将对象转为JSON返回 引入阿里巴巴提供的fastjson工具包 String notloing = JSONObject.toJSONString(error); //响应数据 resp.getWriter().write(notloing); return false; } //放行 log.info("令牌合法,放行"); return true;//放行 } @Override //目标资源方法运行后运行 public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { // HandlerInterceptor.super.postHandle(request, response, handler, modelAndView); System.out.println("postHandle ..."); } @Override //视图渲染完毕后运行, 最后运行 public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { // HandlerInterceptor.super.afterCompletion(request, response, handler, ex); System.out.println("afterCompletion .."); } }
WebConfig.java
package com.example.config; import com.example.interceptor.LoginCheckInterceptor; 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 WebConfig implements WebMvcConfigurer { @Autowired private LoginCheckInterceptor loginCheckInterceptor; @Override //重写方法注册拦截器 public void addInterceptors(InterceptorRegistry registry) { // WebMvcConfigurer.super.addInterceptors(registry); //添加拦截器以及要拦截的资源 registry.addInterceptor(loginCheckInterceptor).addPathPatterns("/**"); } }
运行:
登录获取令牌
查询部门操作:
如果没有写令牌,返回未登录的消息提示
3.异常处理
全局异常处理器
@RestControllerAdvice//异常处理器注解
@ExceptionHandler(Exception.class)//捕获哪一种异常(所有异常)
package com.example.exception; import com.example.entity.Result; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; @RestControllerAdvice//异常处理器注解 public class GobalExceptionHandler { @ExceptionHandler(Exception.class)//捕获所有异常 public Result ex(Exception ex){ ex.printStackTrace(); return Result.error("操作异常,请联系管理员"); } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· DeepSeek在M芯片Mac上本地化部署