外卖心得笔记 - - Day2

JWT令牌

员工登录生成令牌

加载jwtProperties类(该类从配置文件中读取sky.jwt的值),然后调用类中的变量作为
JwtUtil.createJWT()方法的参数,返回一个token作为令牌,后续请求需要携带令牌进行身份验证才能执行业务逻辑。
在这里插入图片描述

        //登录成功后,生成jwt令牌
        Map<String, Object> claims = new HashMap<>();
        claims.put(JwtClaimsConstant.EMP_ID, employee.getId());
        //配置属性类
        String token = JwtUtil.createJWT(
                jwtProperties.getAdminSecretKey(),
                jwtProperties.getAdminTtl(),
                claims);

配置文件:

sky:
  jwt:
    # 设置jwt签名加密时使用的秘钥
    admin-secret-key: itcast
    # 设置jwt过期时间
    admin-ttl: 7200000
    # 设置前端传递过来的令牌名称
    admin-token-name: token


package com.sky.properties;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix = "sky.jwt")
@Data
public class JwtProperties {

    /**
     * 管理端员工生成jwt令牌相关配置
     */
    private String adminSecretKey;
    private long adminTtl;
    private String adminTokenName;

    /**
     * 用户端微信用户生成jwt令牌相关配置
     */
    private String userSecretKey;
    private long userTtl;
    private String userTokenName;

}
    public static String createJWT(String secretKey, long ttlMillis, Map<String, Object> claims) {
        // 指定签名的时候使用的签名算法,也就是header那部分
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
        // 生成JWT的时间
        long expMillis = System.currentTimeMillis() + ttlMillis;
        Date exp = new Date(expMillis);
        // 设置jwt的body
        JwtBuilder builder = Jwts.builder()
                // 如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的
                .setClaims(claims)
                // 设置签名使用的签名算法和签名使用的秘钥
                .signWith(signatureAlgorithm, secretKey.getBytes(StandardCharsets.UTF_8))
                // 设置过期时间
                .setExpiration(exp);
        return builder.compact();
    }

jwt令牌校验的拦截器

一般执行过程为

  1. 客户端请求: 客户端向服务端发送请求,该请求可能包含 JWT 令牌,通常是在请求的头部(HTTP Header)中或作为请求参数。
  2. 拦截器调用阶段: 拦截器通常在请求到达控制器(Controller)之前被调用。在 Spring 框架中,你可以使用HandlerInterceptor 或 Spring Security 中的相关拦截器来实现 JWT 令牌校验。本项目使用的是HandlerInterceptor拦截器
  3. JWT 令牌校验: 在拦截器中,你可以编写代码来提取请求中的 JWT令牌,然后对其进行校验。校验的过程包括验证令牌的签名是否有效、是否过期,以及其他自定义的校验规则。
  4. 通过或拒绝请求: 如果 JWT 令牌校验通过,拦截器可以选择允许请求继续到达控制器;否则,它可以拒绝请求,返回相应的错误信息。
/**
 * jwt令牌校验的拦截器
 */
@Component
@Slf4j
public class JwtTokenAdminInterceptor implements HandlerInterceptor {
    @Autowired
    private JwtProperties jwtProperties;
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //当前线程的id
        System.out.println("当前线程的id:" + Thread.currentThread().getId());
        //判断当前拦截到的是Controller的方法还是其他资源
        if (!(handler instanceof HandlerMethod)) {
            //当前拦截到的不是动态方法,直接放行
            return true;
        }
        //1、从请求头中获取令牌
        String token = request.getHeader(jwtProperties.getAdminTokenName());
        //2、校验令牌
        try {
            log.info("jwt校验:{}", token);
            Claims claims = JwtUtil.parseJWT(jwtProperties.getAdminSecretKey(), token);
            Long empId = Long.valueOf(claims.get(JwtClaimsConstant.EMP_ID).toString());
            log.info("当前员工id:", empId);
            //存储登录用户的令牌
            BaseContext.setCurrentId(empId);
            //3、通过,放行
            return true;
        } catch (Exception ex) {
            //4、不通过,响应401状态码
            response.setStatus(401);
            return false;
        }
    }
}

拦截器会调用JwtUtil.parseJWT(jwtProperties.getAdminSecretKey(), token);使用配置文件中的秘钥进行解密和身份核实。

    public static Claims parseJWT(String secretKey, String token) {
        // 得到DefaultJwtParser
        Claims claims = Jwts.parser()
                // 设置签名的秘钥
                .setSigningKey(secretKey.getBytes(StandardCharsets.UTF_8))
                // 设置需要解析的jwt
                .parseClaimsJws(token).getBody();
        return claims;
    }

使用ThreadLocal从令牌中储存当前登录用户的id,用于后续操作。
BaseContext.setCurrentId(empId);

ThreadLocal 并不是一个Thread,而是Thread的局部变量。
ThreadLocal为每个线程提供单独一份存储空间,具有线程隔离的效果,只有在线程内才能获取到对应的值,线程外	则不能访问。
ThreadLocal常用方法:
public void set(T value) 	设置当前线程的线程局部变量的值
public T get() 		返回当前线程所对应的线程局部变量的值
public void remove()        移除当前线程的线程局部变量
客户端发送的每次请求,后端的Tomcat服务器都会分配一个单独的线程来处理请求

PageHelper插件

	PageHelper是一个用于MyBatis的分页插件,它简化了在数据库查询中进行分页操作的过程。
	使用PageHelper,你可以很容易地在MyBatis查询中加入分页功能,而不需要手动编写复杂的分页逻辑。

注:分页查询插件Pagehelper 基于Mybatis拦截器实现 动态拼接sql

    public PageResult pageQuery(EmployeePageQueryDTO employeePageQueryDTO) {
        //select * from employee limit 0,10; 从第一条开始查我一共查十条
        //使用分页查询插件pagehelper 基于Mybatis拦截器实现 动态拼接sql
        //开始分页查询
        PageHelper.startPage(employeePageQueryDTO.getPage(), employeePageQueryDTO.getPageSize());
        Page<Employee> page = employeeMapper.pageQuery(employeePageQueryDTO);
        long total = page.getTotal();
        List<Employee> records = page.getResult();
        return new PageResult(total , records);
    }

xml文件

    <select id="pageQuery" resultType="com.sky.entity.Employee">
        select * from employee
        <where>
            <if test= " name != null and name != ''">
                and name like concat('%',#{name},'%')
            </if>
        </where>
        order by create_time desc
    </select>

在这里插入图片描述
从图中可以看到当我发起员工分页查询请求,就会先进行jwt校验,然后PageHelper会自动在sql语句后面拼接分页查询条件。

密码处理

当进行根据id查询员工信息时(信息回显),需要加密密码,提高安全性

    public Employee getById(Long id) {
        Employee employee = employeeMapper.getById(id);
        //对密码进行处理
        employee.setPassword("****");
        return employee;
    }

日期格式

如果不设置日期格式,则在传递给前端日期数据时会出现以下情况:

在这里插入图片描述
解决方法:

方式一:在属性上加入注解,对日期进行格式化

    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime createTime;

使用@JsonFormat注解需要引入依赖:

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.9.2</version>
        </dependency>

方式二:在 WebMvcConfiguration 中扩展Spring MVC的消息转换器,统一对日期类型进行格式化处理

//消息转换器
protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        log.info("扩展消息转换器...");
        //创建一个消息转换器对象
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        //需要为消息转换器设置一个对象转换器,对象转化器可以Java对象序列为JSON序列
        converter.setObjectMapper(new JacksonObjectMapper());
        //将自己的消息转换器加入到容器中
        converters.add(0, converter);
        //需要设置优先使用我们的转换器
    }

第一个方法需要在用到日期属性的地方都加入这个注解,第二个方法只需要设置一次就好

  • 46
    点赞
  • 43
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
苍穹外卖数据库的E-R图描述了该数据库中实体之间的关系,E-R图包括实体(Entity)、关系(Relationship)和属性(Attribute)三个要素。苍穹外卖数据库的E-R图主要包含以下几个实体: 1. 用户(User):记录用户的基本信息,包括用户ID、用户名、密码、电话等。 2. 商家(Business):记录商家的基本信息,包括商家ID、商家名、地址、联系电话等。 3. 订单(Order):记录订单的基本信息,包括订单ID、下单时间、送餐地址等。 4. 菜品(Dish):记录菜品的基本信息,包括菜品ID、菜品名、价格、图片等。 5. 购物车(ShoppingCart):记录购物车中的菜品信息,包括购物车ID、用户ID、菜品ID等。 这些实体之间的关系如下: 1. 用户(User)和商家(Business)之间是一对多关系,即一个商家可以有多个用户点餐。 2. 商家(Business)和菜品(Dish)之间是一对多关系,即一个商家可以有多种菜品。 3. 订单(Order)和用户(User)之间是一对多关系,即一个用户可以有多个订单。 4. 订单(Order)和商家(Business)之间是一对多关系,即一个商家可以接收多个订单。 5. 订单(Order)和购物车(ShoppingCart)之间是一对多关系,即一个订单可以对应一个或多个购物车。 基于以上实体和关系,苍穹外卖数据库的E-R图可以用如下方式表示: ``` +-------------+ +-------------+ | User | | Business | +-------------+ +-------------+ | | | | | | | | +--+ +----------------+ | ShoppingCart |--+ +----------------+ | | | | +--+ | Dish | +--------+ ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值