【苍穹外卖 | 篇①】附

在牛某网看见了牛肉哥的帖子之后,打算向牛肉大佬学习,故开始书写优快云博客,通过博客的方式来巩固自身知识学习。

因为之前有粗略的学习了Java Web的基础课程,所以博客内容主要是巩固之前学习当中的模糊点,以及一些自己认为重要的内容,用于自己进一步的掌握开发技能。

目录

完善登录功能:

JWT:

JWT的组成:

Controller层:

补充:

Service层:

Mapper层:

注:

Builder():

@RequestBody:

Swagger的使用:

Swagger:

使用步骤:

引入依赖:

配置knife4j:

设置静态资源映射:

访问测试:

常用注解

未用:

​编辑已用:

完善登录功能:

在苍穹外卖的第一天,完善了登录功能,将密码加密存入数据库,提高安全性。

对于登录验证功能,就要知道JWT(JSON Web Token)

JWT:

JWT将原始的JSON格式数据,转变为字符串,利用一次base64编码
JWT就是将原始的json数据格式进行了安全的封装,这样就可以直接基于jwt在通信双方安全的进行信息传输。

JWT的组成:

(JWT令牌由三个部分组成,三个部分之间使用英文的点来分割)

  • 第一部分:Header(头), 记录令牌类型、签名算法等。

  • 第二部分:Payload(有效载荷),携带一些自定义信息、默认信息等。

  • 第三部分:Signature(签名),防止Token被篡改、确保安全性。将header、payload,并加入指定秘钥,通过指定签名算法计算而来。

Controller层:

首先来看Controller层,

  1. 首先是调用login函数去查询数据库,返回到employee,包含一些用户信息,详看该数据表
  2. 创建集合claims,存储键值对
  3. 新增键值对:empid--getid()
  4. 生成JWT令牌:它将一个保密的密钥(用于签名)、一个有效期(用于控制 Token 时效性)、一组自定义的用户信息(Claims,用于在服务间传递身份和权限)结合起来,生成了一个唯一的、可验证的、有期限的 JWT 字符串。
  5. 返回一个employeeLoginVO
/**
     * 登录
     *
     * @param employeeLoginDTO
     * @return
     */
    @PostMapping("/login")
    public Result<EmployeeLoginVO> login(@RequestBody EmployeeLoginDTO employeeLoginDTO) {
        log.info("员工登录:{}", employeeLoginDTO);
		//调用service方法查询数据库
        Employee employee = employeeService.login(employeeLoginDTO);

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

        EmployeeLoginVO employeeLoginVO = EmployeeLoginVO.builder()
                .id(employee.getId())
                .userName(employee.getUsername())
                .name(employee.getName())
                .token(token)
                .build();

        return Result.success(employeeLoginVO);
    }

补充:

细看JwtUtil类:

创建JWT:需要自定义的claims信息,需要签名算法与签名秘钥。设置过期时间

package com.sky.utils;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.Map;

public class JwtUtil {
    /**
     * 生成jwt
     * 使用Hs256算法, 私匙使用固定秘钥
     *
     * @param secretKey jwt秘钥
     * @param ttlMillis jwt过期时间(毫秒)
     * @param claims    设置的信息
     * @return
     */
    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();
    }

    /**
     * Token解密
     *
     * @param secretKey jwt秘钥 此秘钥一定要保留好在服务端, 不能暴露出去, 否则sign就可以被伪造, 如果对接多个客户端建议改造成多个
     * @param token     加密后的token
     * @return
     */
    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;
    }

}

Service层:

再来看Service的login方法:

  1. 先获取用户在浏览器中输入的用户名与密码
  2. 调用mapper层的getbyusername函数,获取数据库中的相应信息
  3. 比对信息,返回不同情况的信息
/**
     * 员工登录
     *
     * @param employeeLoginDTO
     * @return
     */
    public Employee login(EmployeeLoginDTO employeeLoginDTO) {
        String username = employeeLoginDTO.getUsername();
        String password = employeeLoginDTO.getPassword();

        //1、根据用户名查询数据库中的数据
        Employee employee = employeeMapper.getByUsername(username);

        //2、处理各种异常情况(用户名不存在、密码不对、账号被锁定)
        if (employee == null) {
            //账号不存在
            throw new AccountNotFoundException(MessageConstant.ACCOUNT_NOT_FOUND);
        }

        //密码比对
        if (!password.equals(employee.getPassword())) {
            //密码错误
            throw new PasswordErrorException(MessageConstant.PASSWORD_ERROR);
        }

        if (employee.getStatus() == StatusConstant.DISABLE) {
            //账号被锁定
            throw new AccountLockedException(MessageConstant.ACCOUNT_LOCKED);
        }

        //3、返回实体对象
        return employee;
    }

Mapper层:

最后看mapper层的代码:

    @Select("select * from employee where username = #{username}"):返回查询到的对应一行

package com.sky.mapper;

import com.sky.entity.Employee;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

@Mapper
public interface EmployeeMapper {

    /**
     * 根据用户名查询员工
     * @param username
     * @return
     */
    @Select("select * from employee where username = #{username}")
    Employee getByUsername(String username);

}

注:

Builder():

它为你提供了一个清晰、灵活、易读的方式来一步步配置和构建一个完整的 JWT 令牌。你通过链式调用设置 JWT 的各个部分,最后调用 .compact() 方法完成构建并获取最终的JWT 字符串。

@RequestBody:

将HTTP请求体中的原始数据(如JSON字符串)自动转换为Java对象,无需手动解析。
不适用于单字段参数‌:若仅需接收单个字段,应使用:RequestParam

Swagger的使用:

Swagger:

是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务
knife4j是为Java MVC框架集成Swagger生成Api文档的增强解决方案,

使用步骤:

引入依赖:

<dependency>
   <groupId>com.github.xiaoymin</groupId>
   <artifactId>knife4j-spring-boot-starter</artifactId>
</dependency>

配置knife4j:


    @Bean
    public Docket docket() {
        ApiInfo apiInfo = new ApiInfoBuilder()
                .title("苍穹外卖项目接口文档")
                .version("2.0")
                .description("苍穹外卖项目接口文档")
                .build();
//创建一个 ApiInfo 对象,该对象包含了 API 文档的元信息,会显示在生成的文档网页的顶部。
        Docket docket = new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo)
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.sky.controller"))
//指定了 Springfox 要扫描哪个包下的 @Controller 类。
                .paths(PathSelectors.any())
                .build();
        return docket;
    }

当你启动 Spring Boot 应用时,Spring 会发现这个 docket() 方法,执行它并获取 Docket 实例。Springfox-Swagger 会根据 Docket 中的配置,自动扫描 com.sky.controller 包下的所有 Controller,分析其中的接口和注解(包括参数、返回值、注释等),然后生成一个符合 Swagger 2.0 规范的 JSON 文档。

同时,它还会提供一个默认的 UI 界面(通常在 /swagger-ui.html/swagger-ui/ 路径下),你可以通过浏览器访问这个地址,看到一个交互式的 API 文档网页。在这个网页上,你可以:

  • 浏览所有已定义的 API 接口。
  • 查看每个接口的详细信息,包括 HTTP 方法、URL、请求参数、响应状态码等。
  • 直接在网页上点击 “Try it out” 来发送 API 请求,测试接口功能。

设置静态资源映射:

protected void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/doc.html").addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
}

这段代码的核心作用是配置 Spring MVC 的静态资源处理器,它允许浏览器通过 URL 直接访问Swagger UI,目的是能够通过浏览器访问 API 文档的 Web 界面。
当客户端(浏览器)请求 /doc.html 这个 URL 时,去 classpath(类路径)下的 /META-INF/resources/ 目录中查找名为 doc.html 的文件,并把它返回给浏览器
浏览器在加载 doc.html 页面后,页面中的 JavaScript 和 CSS 文件可能会请求类似 /webjars/swagger-ui/4.15.5/swagger-ui-bundle.js 这样的 URL。这个请求会匹配到 /webjars/** 规则。

访问测试:

接口文档访问路径为 http://ip:port/doc.html

常用注解

通过注解可以控制生成的接口文档,常用注解如下:

注解说明
@Api用在类上,例如Controller,表示对类的说明,tag=" "
@ApiModel用在类上,例如entity、DTO、VO description = " "
@ApiModelProperty用在属性上,描述属性信息
@ApiOperation用在方法上,例如Controller的方法,说明方法的用途、作用 value=" "

这些注解是用于方便显示与理解的,这里不做过多解释,需要知道各注解所用位置。

下面是对比图:

未用:

已用:

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值