杀鸡焉用牛刀 发表于 2024-9-1 09:46:13

SpringBoot+Vue3项目实战——SpringBoot篇

项目预备与配置


数据库以及其他项目配置

server:
port: 9090

spring:
datasource:
    url: jdbc:mysql://localhost:3306/lostarknote
    username: root
    password: guxiang
    driver-class-name: com.mysql.cj.jdbc.Driver

mybatis:
configuration:
    map-underscore-to-camel-case: true遇到题目:找不到"url"....
可能原因:没有扫描到配置文件(yml),在pom.xml中的build标签中添加以下内容
<resources>
    <resource>
      <directory>src/main/java</directory>
      <includes>
            <include>**/*.yml</include>
            <include>**/*.properties</include>
            <include>**/*.xml</include>
      </includes>
      <filtering>false</filtering>
    </resource>
    <resource>
      <directory>src/main/resources</directory>
      <includes>
            <include>**/*.yml</include>
            <include>**/*.properties</include>
            <include>**/*.xml</include>
      </includes>
      <filtering>false</filtering>
    </resource>
</resources>
启动类相关

@SpringBootApplication:包扫描时,自动扫描启动类所在包及其子包,若需要扫描其他包,需要使用@ComponentScan进行包名指定

Lombok工具:

自动生成getter/setter/toString方法 ,需要引入lombok依赖,然后在实体类上添加@Data注解
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.30</version>
</dependency>
Result实体类

@NoArgsConstructor
@AllArgsConstructor
@Data
public class Result<T> {

    private Integer code;
    private String message;
    private T data;

    public static <E> Result<E> success(E data)
    {
      return new Result<>(0,"操作成功",data);
    }

    public static Result success(){
      return new Result(0,"操作成功",null);
    }

    public static Result error(String message) {
      return new Result(1,message,null);
    }


}
用户模块


注册功能:


1.密码加密

public class MD5Util {
    //生成MD5
    public static String getMD5(String message) {
      String md5 = "";
      try {
            MessageDigest md = MessageDigest.getInstance("MD5");// 创建一个md5算法对象
            byte[] messageByte = message.getBytes("UTF-8");
            byte[] md5Byte = md.digest(messageByte);            // 获得MD5字节数组,16*8=128位
            md5 = bytesToHex(md5Byte);                            // 转换为16进制字符串
      } catch (Exception e) {
            e.printStackTrace();
      }
      return md5;
    }

    // 二进制转十六进制
    public static String bytesToHex(byte[] bytes) {
      StringBuffer hexStr = new StringBuffer();
      int num;
      for (int i = 0; i < bytes.length; i++) {
            num = bytes;
            if(num < 0) {
                num += 256;
            }
            if(num < 16){
                hexStr.append("0");
            }
            hexStr.append(Integer.toHexString(num));
      }
      return hexStr.toString().toUpperCase();
    }
}
2.参数校验——Spring Validation

https://cdn.nlark.com/yuque/0/2024/png/27509659/1724488370258-2fff5ce4-4b4f-4f36-9081-9bc37070d6bb.png#averageHue=%23faf9f9&clientId=u49647e4d-60aa-4&from=paste&height=322&id=ZWdio&originHeight=643&originWidth=1434&originalType=binary&ratio=1&rotation=0&showTitle=false&size=374144&status=done&style=none&taskId=u2ec4fd8c-0f89-419f-b0c7-11f01e8c88a&title=&width=717

3.全局异常处理器

@RestControllerAdvice
public class GobalExceptionHandler {

    @ExceptionHandler(Exception.class)
    public Result handleException(Exception e)
    {
      e.printStackTrace();
      return Result.error(StringUtils.hasLength(e.getMessage())? e.getMessage() : "操作失败!");
    }
}
登录功能


登录认证:JWT令牌

https://cdn.nlark.com/yuque/0/2024/png/27509659/1724489556568-920ef5bd-8334-41de-973d-78709796c5ad.png#averageHue=%23fbf9eb&clientId=u49647e4d-60aa-4&from=paste&height=346&id=u38cdaafe&originHeight=692&originWidth=1456&originalType=binary&ratio=1&rotation=0&showTitle=false&size=402509&status=done&style=none&taskId=u50afbe8a-900d-4dc7-9ff7-862aff42db9&title=&width=728
https://cdn.nlark.com/yuque/0/2024/png/27509659/1724489761881-ba8ccc2c-8a2c-42c8-ad50-b3e93cbd0460.png#averageHue=%23f9f7dc&clientId=u49647e4d-60aa-4&from=paste&height=249&id=uf36d9abc&originHeight=497&originWidth=1388&originalType=binary&ratio=1&rotation=0&showTitle=false&size=307408&status=done&style=none&taskId=u53ed3cc8-741d-44bb-810f-7ec2d992e8f&title=&width=694

JWT工具类

public class JWTUtil {
    private static final String SECRET = "guxiang";

    public static String generateToken(Map<String, Object> claims){
      return JWT.create()
                .withClaim("claims", claims)
                .withExpiresAt(new Date(System.currentTimeMillis()* 1000 * 60 * 60 * 24))
                .sign(Algorithm.HMAC256(SECRET));
    }

    public static Map<String, Object> parseToken(String token){
      return JWT.require(Algorithm.HMAC256(SECRET))
                .build()
                .verify(token)
                .getClaim("claims")
                .asMap();
    }
}
JWT验证:从请求头中获取token

@RestController
@RequestMapping("/dungeon")
public class DungeonController {

    @GetMapping("/list")
    public Result<String> list(@RequestHeader("Authorization") String token, HttpServletResponse response) {
      //验证token
      //若能正常解析(不报错),则验证通过
      try {
            Map<String, Object> claims = JWTUtil.parseToken(token);
            return Result.success("副本数据获取成功!");
      }catch (Exception e) {
            response.setStatus(401);
            return Result.error("未登录!");
      }

    }
}
进阶:注册拦截器进行验证。

注册一个拦截器进行token的验证,就不用单独在每个业务代码里进行token验证
@Component
public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
      //令牌验证
      String token = request.getHeader("Authorization");

      //若能正常解析(不报错),则验证通过
      try {
            Map<String, Object> claims = JWTUtil.parseToken(token);
            //放行
            return true;
      }catch (Exception e) {
            response.setStatus(401);
            return false;
      }
    }
}@Configuration
public class WebConfigimplements WebMvcConfigurer {

    @Autowired
    private LoginInterceptor loginInterceptor;
   
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
      //登录注册不用拦截
      registry.addInterceptor(loginInterceptor).excludePathPatterns("/user/login","/user/register");
    }
}@RestController
@RequestMapping("/dungeon")
public class DungeonController {

    @GetMapping("/list")
    public Result<String> list() {
      //这里不用再验证token,已经统一在拦截器中做验证
      return Result.success("副本数据获取成功!");
    }
}
登录优化——Redis

https://cdn.nlark.com/yuque/0/2024/png/27509659/1724726267565-351696e4-6933-46fc-917f-64ff425ea432.png#averageHue=%23f3f2f2&clientId=u61fe0d29-1cc1-4&from=paste&height=175&id=uae1f345c&originHeight=350&originWidth=1746&originalType=binary&ratio=2&rotation=0&showTitle=false&size=198953&status=done&style=none&taskId=u491c81bc-e20f-4a79-be30-cd4d1bcb869&title=&width=873
https://cdn.nlark.com/yuque/0/2024/png/27509659/1724726416408-be6d8fb1-7f06-474d-9b95-8319c95d3047.png#averageHue=%23f9f9f6&clientId=u61fe0d29-1cc1-4&from=paste&height=436&id=ub7d169a4&originHeight=872&originWidth=1698&originalType=binary&ratio=2&rotation=0&showTitle=false&size=545601&status=done&style=none&taskId=u0b3ce942-0e7e-464e-b421-d426ec6296e&title=&width=849

获取用户信息


ThreadLocal


[*]获取用户信息需要通过token中存储的username到数据库中查找,需要解析token。
[*]在其他业务可能也需要username的信息,又要解析token,为了避免代码重复,在之前拦截器中解析出的token同一放到线程中(ThreadLocal)。
https://cdn.nlark.com/yuque/0/2024/png/27509659/1724491870223-7d0ab1ec-7a6e-48c4-8e01-b58743ec88cf.png#averageHue=%23ede7e4&clientId=u49647e4d-60aa-4&from=paste&height=363&id=ucf925ff5&originHeight=725&originWidth=697&originalType=binary&ratio=1&rotation=0&showTitle=false&size=158027&status=done&style=none&taskId=u8743cc65-bf61-4218-a89d-49d826f6bed&title=&width=348.5
public class ThreadLocalUtil {

    //提供ThreadLocal对象
    private static final ThreadLocal THREAD_LOCAL= new ThreadLocal();

    public static <T> T get() {
      return (T) THREAD_LOCAL.get();
    }

    public static void set(Object value) {
      THREAD_LOCAL.set(value);
    }

    public static void remove() {
      THREAD_LOCAL.remove();
    }
}拦截器中将数据存入ThreadLocal中,请求完成后清除ThreadLocal,防止内存泄漏
@Component
public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
      //令牌验证
      String token = request.getHeader("Authorization");



      //若能正常解析(不报错),则验证通过
      try {
            Map<String, Object> claims = JWTUtil.parseToken(token);
            //将数据存储到ThreadLocal中
            ThreadLocalUtil.set(claims);
            //放行
            return true;
      }catch (Exception e) {
            response.setStatus(401);
            return false;
      }
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
      //清空ThreadLocal中的数据,防止内存泄露
      ThreadLocalUtil.remove();
    }
}接口中从ThreadLocal中获取数据
@GetMapping("/userInfo")
    public Result<User> userInfo(@RequestHeader("Authorization") String token) {
//      Map<String, Object> claims = JWTUtil.parseToken(token);
//      String username = (String) claims.get("username");

      Map<String, Object> map = ThreadLocalUtil.get();
      String username = (String) map.get("username");

      User loginUser = userService.findByUsername(username);

      return Result.success(loginUser);
    }
@JsonIgnore

https://cdn.nlark.com/yuque/0/2024/png/27509659/1724491443923-60b7f0f7-1830-4b37-ac0d-463cc4054091.png#averageHue=%23f4f0df&clientId=u49647e4d-60aa-4&from=paste&height=42&id=tFG7D&originHeight=52&originWidth=925&originalType=binary&ratio=1&rotation=0&showTitle=false&size=46240&status=done&style=none&taskId=ub5d6ceeb-108b-4f28-ae6b-7226e15e2a2&title=&width=740.5

更新用户信息


实体类中参数验证

https://cdn.nlark.com/yuque/0/2024/png/27509659/1724493311830-36c13ab6-ab03-4d4b-a3c2-1feffe42d2eb.png#averageHue=%23e2cac4&clientId=u49647e4d-60aa-4&from=paste&height=323&id=uf127c2df&originHeight=645&originWidth=1385&originalType=binary&ratio=1&rotation=0&showTitle=false&size=400992&status=done&style=none&taskId=u4b50e1c4-a384-455b-83d8-94fce765b43&title=&width=692.5

更新用户密码

参数用一个map接收,因为继续的参数名与数据库中不一致
更新用户信息时,参数名与字段名一致,所以用user实体类接收
https://cdn.nlark.com/yuque/0/2024/png/27509659/1724493923725-46d7e988-17dc-4f9e-a9eb-1c3f5903a6d0.png#averageHue=%23fac9b1&clientId=u49647e4d-60aa-4&from=paste&height=93&id=u2b3a0ab8&originHeight=185&originWidth=1089&originalType=binary&ratio=1&rotation=0&showTitle=false&size=61591&status=done&style=none&taskId=ub00a4cd7-67c9-4524-bd3c-1df01ab0708&title=&width=544.5

文章模块


新增文章分类


文章分类列表


@JsonFormat

https://cdn.nlark.com/yuque/0/2024/png/27509659/1724679349182-f53100b5-2caf-4d90-9cfd-042ef4b14959.png#averageHue=%23ecefdc&clientId=u61fe0d29-1cc1-4&from=paste&height=67&id=u10f0438c&originHeight=134&originWidth=568&originalType=binary&ratio=2&rotation=0&showTitle=false&size=99538&status=done&style=none&taskId=ua5905ecd-5d4b-4e8a-ab80-996d13e8858&title=&width=284

文章分类详情


更新文章分类

https://cdn.nlark.com/yuque/0/2024/png/27509659/1724680351712-f2e307a6-5874-4c28-b070-6a1527c03e6b.png#averageHue=%23f7f7f6&clientId=u61fe0d29-1cc1-4&from=paste&height=445&id=u485131d3&originHeight=889&originWidth=1415&originalType=binary&ratio=2&rotation=0&showTitle=false&size=521179&status=done&style=none&taskId=u6e0ee40e-09f4-436d-9ce0-878d99ec623&title=&width=707.5

新增文章


自定义校验

https://cdn.nlark.com/yuque/0/2024/png/27509659/1724719296719-aba8d69b-d381-4f3f-8ba1-73c49c79816f.png#averageHue=%23ebebe2&clientId=u61fe0d29-1cc1-4&from=paste&height=450&id=u0f5a29bb&originHeight=899&originWidth=1592&originalType=binary&ratio=2&rotation=0&showTitle=false&size=832166&status=done&style=none&taskId=ub3ede5b0-8184-4ea3-a0ae-4fba6df3a83&title=&width=796

文章分页查询

https://cdn.nlark.com/yuque/0/2024/png/27509659/1724720216592-e3548263-016c-4a8c-b007-27210f9bc731.png#averageHue=%23fbfbf9&clientId=u61fe0d29-1cc1-4&from=paste&height=128&id=u7528f9d5&originHeight=256&originWidth=527&originalType=binary&ratio=2&rotation=0&showTitle=false&size=77533&status=done&style=none&taskId=u22748546-e440-49ca-b979-d63a1bd3b05&title=&width=263.5
https://cdn.nlark.com/yuque/0/2024/png/27509659/1724720365010-4ff7382b-e85c-4f6b-82e7-4bf81bbc2f75.png#averageHue=%23f8f5e7&clientId=u61fe0d29-1cc1-4&from=paste&height=101&id=ua481604d&originHeight=202&originWidth=742&originalType=binary&ratio=2&rotation=0&showTitle=false&size=112483&status=done&style=none&taskId=uda08f4b9-11ec-40bc-89c7-6a0a4dba6d1&title=&width=371
https://cdn.nlark.com/yuque/0/2024/png/27509659/1724720426641-af539793-2c2b-4302-93e7-8d86af685a69.png#averageHue=%23f5f4f1&clientId=u61fe0d29-1cc1-4&from=paste&height=47&id=u42300824&originHeight=93&originWidth=487&originalType=binary&ratio=2&rotation=0&showTitle=false&size=31870&status=done&style=none&taskId=u828bfa76-790d-47a9-8480-e491fc277c4&title=&width=243.5
https://cdn.nlark.com/yuque/0/2024/png/27509659/1724720549882-4af8332e-ec49-482e-987c-a94abc19572b.png#averageHue=%23f8f7f4&clientId=u61fe0d29-1cc1-4&from=paste&height=100&id=u80b92d17&originHeight=199&originWidth=772&originalType=binary&ratio=2&rotation=0&showTitle=false&size=82885&status=done&style=none&taskId=u7ee6ddd4-0326-48c3-bb12-5abfa4eb631&title=&width=386

文件上传

https://cdn.nlark.com/yuque/0/2024/png/27509659/1724722208238-5ceb4e03-92ee-4878-b928-c30a97225967.png#averageHue=%23fbeadc&clientId=u61fe0d29-1cc1-4&from=paste&height=375&id=u6953d55a&originHeight=749&originWidth=1499&originalType=binary&ratio=2&rotation=0&showTitle=false&size=395148&status=done&style=none&taskId=u68841709-8f89-4e8e-9758-d898fcb8bff&title=&width=749.5





免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: SpringBoot+Vue3项目实战——SpringBoot篇