每日总结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 Web Tokens - jwt.io

定义了一种简洁的、自包含的格式,用于在通信双方以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("操作异常,请联系管理员");

    }


}
复制代码
posted @   意い十三章  阅读(2)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· DeepSeek在M芯片Mac上本地化部署
点击右上角即可分享
微信分享提示