当前位置: 首页 > web >正文

springboot实战篇1

1. 环境搭建

执行资料中的big_event.sql脚本,准备数据库表
创建springboot工程,引入对应的依赖(web、mybatis、mysql驱动)
配置文件application.yml中引入mybatis的配置信息
创建包结构,并准备实体类
在这里插入图片描述

1.1准备数据库

-- 使用数据库
use big_event;
alter database big_event character set utf8;
-- 用户表
create table user (id int unsigned primary key auto_increment comment 'ID',username varchar(20) not null unique comment '用户名',password varchar(32)  comment '密码',nickname varchar(10)  default '' comment '昵称',email varchar(128) default '' comment '邮箱',user_pic varchar(128) default '' comment '头像',create_time datetime not null comment '创建时间',update_time datetime not null comment '修改时间'
) comment '用户表';-- 分类表
create table category(id int unsigned primary key auto_increment comment 'ID',category_name varchar(32) not null comment '分类名称',category_alias varchar(32) not null comment '分类别名',create_user int unsigned not null comment '创建人ID',create_time datetime not null comment '创建时间',update_time datetime not null comment '修改时间',constraint fk_category_user foreign key (create_user) references user(id) -- 外键约束
);
-- 文章表
create table article(id int unsigned primary key auto_increment comment 'ID',title varchar(30) not null comment '文章标题',content varchar(10000) not null comment '文章内容',cover_img varchar(128) not null  comment '文章封面',state varchar(10) default '草稿' comment '文章状态: 只能是[已发布] 或者 [草稿]',category_id int unsigned comment '文章分类ID',create_user int unsigned not null comment '创建人ID',create_time datetime not null comment '创建时间',update_time datetime not null comment '修改时间',constraint fk_article_category foreign key (category_id) references category(id),-- 外键约束constraint fk_article_user foreign key (create_user) references user(id) -- 外键约束
)

1.2创建springboot项目 导入对应的依赖:

在这里插入图片描述

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.2.3</version></parent><groupId>com.itheima</groupId><artifactId>big-event</artifactId><version>1.0-SNAPSHOT</version><packaging>jar</packaging><name>big-event</name><url>http://maven.apache.org</url><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><dependencies>
<!--   web依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- mybatis依赖--><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>3.0.4</version></dependency><!-- mysql驱动--><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>3.8.1</version><scope>test</scope></dependency><!--      lombok依赖--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><!--      validator依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId></dependency><!--      java-jwt坐标--><dependency><groupId>com.auth0</groupId><artifactId>java-jwt</artifactId><version>4.4.0</version></dependency>
<!--  单元测试的坐标--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId></dependency></dependencies></project>

1.3appliction.yml中引入mybatis的配置信息:

spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/big_eventusername: rootpassword: 1234mybatis:configuration:map-underscore-to-camel-case: true  # 开启驼峰命名和下划线命名的转换

1.4创建包结构,准备实体类:

在这里插入图片描述

1.5编写启动类

package com.itheima;import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;/*** Hello world!**/
@SpringBootApplication
@MapperScan("com.itheima.mapper")
public class BigEventAppliction
{public static void main( String[] args ){SpringApplication.run(BigEventAppliction.class,args);}
}

2.注册接口的开发:

在这里插入图片描述

Result

在这里插入图片描述

2.1controller.UserController:

@Validated
@RestController
@RequestMapping("/user")
public class UserController {@Autowiredprivate UserService userService;@PostMapping("/register")public Result register(@Pattern(regexp = "^\\S{5,16}$") String username, @Pattern(regexp = "^\\ S{5,16}$") String password) {User u =  userService.findByUsername(username);if (u == null){//没有占用//注册userService.register(username,password);return Result.success();}else {//占用return Result.error("用户名已被占用");}}}

2.2service.impl.UserServiceImpl

package com.itheima.service.impl;import com.itheima.mapper.UserMapper;
import com.itheima.pojo.User;
import com.itheima.service.UserService;
import com.itheima.utils.Md5Util;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class UserServiceImpl implements UserService {@Autowiredprivate UserMapper userMapper;@Overridepublic User findByUsername(String username) {User u = userMapper.findByUserName(username);return u;}@Overridepublic void register(String username, String password) {//加密String md5String = Md5Util.getMD5String(password);//添加userMapper.add(username,md5String);}
}

2.3mapper.UserMapper

package com.itheima.mapper;import com.itheima.pojo.User;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;@Mapper
public interface UserMapper {//根据用户名查询用户信息@Select("select * from user where username = #{username}")User findByUserName(String username);//添加用户信息@Insert("insert into user(username,password,create_time,update_time)" +" values(#{username},#{password},now(),now())")void add(String username, String password);
}

2.4接口测试工具Apifox

  • 具体操作可以查看Apifox帮助文档:
    Apifox界面:
    在这里插入图片描述
    数据库界面:注册成功
    在这里插入图片描述
    注意在Result类中需要添加@Data注解
    在这里插入图片描述

2.5参数校验:

在这里插入图片描述
参数过于繁琐的解决方法:
Spring Validation
Spring 提供的一个参数校验框架,使用预定义的注解完成参数校验

2.5.1使用步骤:

    1. 引入Spring Validation 起步依赖
    1. 在参数前面添加@Pattern注解
    1. 在Controller类上添加@Validated注解
<!--      validator依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId></dependency>
@Pattern(regexp = "^\\S{5,16}$") String username, @Pattern(regexp = "^\\S{5,16}$") String password	 

2.6 全局异常处理器:

exception. GlobalExceptionHandler

package com.itheima.exception;import com.itheima.pojo.Result;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;@RestControllerAdvice
public class GlobalExceptionHandler {@ExceptionHandler(Exception.class)public Result handleExcpetion(Exception e){e.printStackTrace();return Result.error(StringUtils.hasLength(e.getMessage())?e.getMessage():"操作失败");}
}

3.登录

3.1UserController

 @PostMapping("/login")public Result login(@Pattern(regexp = "^\\S{5,16}$") String username, @Pattern(regexp = "^\\S{5,16}$") String password){//根据y用户名查询用户信息User loginUser = userService.findByUsername(username);//判断用户是否存在if(loginUser == null){//不存在return Result.error("用户名错误");}//判断密码是否正确,LoginUser对象中的password是密文if(Md5Util.getMD5String(password).equals(loginUser.getPassword())){Map<String,Object> claims = new HashMap<>();claims.put("id",loginUser.getId());claims.put("username",loginUser.getUsername());String token = JwtUtil.genToken(claims);return Result.success(token);}return Result.error("密码错误");}

3.2登录认证:

一般情况下要先完成登录接口完成之后才能进入其他接口,所以其他接口就需要在提供服务之前,对登录状态进行一个检查–登录认证;

3.2.1先创建article类;

package com.itheima.controller;import com.itheima.pojo.Result;
import com.itheima.utils.JwtUtil;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.bind.annotation.*;import java.util.Map;@RestController
@RequestMapping("/article")
public class ArticleController {@GetMapping("/list")public Result<String> list(/*@RequestHeader(name = "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("未登录");
//        }return Result.success("文章列表查询成功");}
}

解决方法:令牌

令牌就是一段字符串

  • 承载业务数据, 减少后续请求查询数据库的次数
  • 防篡改, 保证信息的合法性和有效性

JWT令牌

在这里插入图片描述

1.令牌的生成;
  • 导入依赖:
<!--      java-jwt坐标--><dependency><groupId>com.auth0</groupId><artifactId>java-jwt</artifactId><version>4.4.0</version></dependency>
  • 生成JWT
@Testpublic void testGen(){Map<String,Object> claims = new HashMap<>();claims.put("id",1);claims.put("username","张三");//生成jwt代码String token = JWT.create().withClaim("user",claims)//添加载荷.withExpiresAt(new Date(System.currentTimeMillis()+1000*60*60*12))//设置过期时间.sign(Algorithm.HMAC256("itheima"));//设置签名算法和密钥System.out.println(token);}
  • 验证:
 @Testpublic void testParse(){//定义字符串,模拟用户传递过来的tokenString token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9" +".eyJ1c2VyIjp7ImlkIjoxLCJ1c2VybmFtZSI6IuW8oOS4iSJ9LCJleHAiOjE3NTMwNjk2OTh9" +".kG5LboxQ_56hrtRxc0uETTVgjrpN_ngWA5ush1scB8M";JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256("itheima")).build();DecodedJWT decodedJWT = jwtVerifier.verify(token);//验证token,生成一个解析后的JWT对象Map<String, Claim> claims = decodedJWT.getClaims();System.out.println(claims.get("user "));//如果纂改了头部和载荷部分的数据,那么验证失败//如果密钥改了 验证失败//如果token过期了 验证失败}

注意:JWT验证时使用的签名秘钥,必须和生成JWT令牌时使用的秘钥是配套的。
如果JWT令牌解析时报错,则说明 JWT令牌被篡改 或 失效了,令牌非法。

    @PostMapping("/login")public Result login(@Pattern(regexp = "^\\S{5,16}$") String username, @Pattern(regexp = "^\\S{5,16}$") String password){//根据y用户名查询用户信息User loginUser = userService.findByUsername(username);//判断用户是否存在if(loginUser == null){//不存在return Result.error("用户名错误");}//判断密码是否正确,LoginUser对象中的password是密文if(Md5Util.getMD5String(password).equals(loginUser.getPassword())){Map<String,Object> claims = new HashMap<>();claims.put("id",loginUser.getId());claims.put("username",loginUser.getUsername());String token = JwtUtil.genToken(claims);return Result.success(token);}return Result.error("密码错误");}@GetMapping("/userInfo")public Result<User> userInfo(/*@RequestHeader(name = "Authorization")String token*/){//根据用户名查询用户
//        Map<String,Object> map = JwtUtil.parseToken(token);
//        String username = (String) map.get("username");Map<String,Object> map =  ThreadLocalUtil.get();String  username =  (String) map.get("username");User user = userService.findByUsername(username);return Result.success(user);}

拦截器的完成

因为后续会有多个接口完成相同的操作那么就可以使用拦截器来统一进行操作;
在这里插入图片描述

具体实现代码:

1.拦截器的代码:interceptors.LoginInterceptor:

package com.itheima.interceptors;import com.itheima.pojo.Result;
import com.itheima.utils.JwtUtil;
import com.itheima.utils.ThreadLocalUtil;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;import java.util.Map;@Component
public class LoginInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response,Object handler) throws Exception{//令牌的验证String token = request.getHeader("Authorization");//验证tokentry {Map<String,Object> claims = JwtUtil.parseToken(token);//把业务数据存储到ThreadLocal中ThreadLocalUtil.set(claims);//放行return true;} catch (Exception e) {response.setStatus(401);//不放行return false;}}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {//清空Threadlocal的数据ThreadLocalUtil.remove();}}

2.注册拦截器:config.WebConfig:

package com.itheima.config;import com.itheima.interceptors.LoginInterceptor;
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 {@Autowiredprivate LoginInterceptor loginInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry){//登录接口和注册接口不拦截registry.addInterceptor(loginInterceptor).excludePathPatterns("/user/login","/user/register");}
}

4、获取用户详细信息

4.1UserController

    @GetMapping("/userInfo")public Result<User> userInfo(/*@RequestHeader(name = "Authorization")String token*/){//根据用户名查询用户
//        Map<String,Object> map = JwtUtil.parseToken(token);
//        String username = (String) map.get("username");Map<String,Object> map =  ThreadLocalUtil.get();String  username =  (String) map.get("username");User user = userService.findByUsername(username);return Result.success(user);}

4.2在Apifox整体添加请求头:

在这里插入图片描述
Autheriation是请求头的名字,冒号后面是值

4.3响应的时候不让某些元素显示:

在这里插入图片描述

4.4注意开启驼峰命名和下划线命名的转换

mybatis:configuration:map-underscore-to-camel-case: true  # 开启驼峰命名和下划线命名的转换

5.优化代码

ThreadLocal

提供线程局部变量
用来存取数据: set()/get()
使用ThreadLocal存储的数据, 线程安全

在这里插入图片描述

1.在LoginInterceptor中存入ThreadLocal

    Map<String,Object> claims = JwtUtil.parseToken(token);//把业务数据存储到ThreadLocal中ThreadLocalUtil.set(claims);

2.在UserController使用

    @GetMapping("/userInfo")public Result<User> userInfo(/*@RequestHeader(name = "Authorization")String token*/){//根据用户名查询用户
//        Map<String,Object> map = JwtUtil.parseToken(token);
//        String username = (String) map.get("username");Map<String,Object> map =  ThreadLocalUtil.get();String  username =  (String) map.get("username");User user = userService.findByUsername(username);return Result.success(user);}

3.清空ThreadLocal中的数据 防止数据泄露

  @Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {//清空Threadlocal的数据ThreadLocalUtil.remove();}
http://www.xdnf.cn/news/16037.html

相关文章:

  • 基于 HAProxy 搭建 EMQ X 集群
  • C++的“链”珠妙笔:list的编程艺术
  • 解决vscode中vue格式化后缩进太小的问题,并去除分号 - 设置Vetur tabSize从2到4,设置prettier取消分号semi
  • 计算机发展史:人工智能时代的智能变革与无限可能
  • 基于WebSocket的安卓眼镜视频流GPU硬解码与OpenCV目标追踪系统实现
  • 【PTA数据结构 | C语言版】哥尼斯堡的“七桥问题”
  • C# Lambdab表达式 Var 类
  • Elupload实现多个文件上传与已上传列表中做对比,若重复则只保留已上传列表中的数据,同时告诉用户,有哪些文件重复上传了
  • 搭建种草商城框架指南
  • 飞算科技:以原创技术为翼,赋能产业数字化转型
  • Linux第三课:需要自己安装的远程登录工具PuTTY的介绍
  • 【PTA数据结构 | C语言版】求单源最短路的Dijkstra算法
  • Taro 本地存储 API 详解与实用指南
  • G7打卡——Semi-Supervised GAN
  • EMBMS1820芯祥科技18单元电池监控器芯片数据手册
  • 华控的科技布局——全球化战略与合作生态
  • 力扣(LeetCode)第 161 场双周赛
  • macbookpro m1 max本儿上速搭一个elasticsearch+kibana环境
  • 基于deepseek的LORA微调
  • 【设计模式C#】简单工厂模式(用于简化获取对象实例化的复杂性)
  • 个人中心产品设计指南:从信息展示到用户体验的细节把控
  • mongodb源代码分析createCollection命令由create.idl变成create_gen.cpp过程
  • 在.NET Core API 微服务中使用 gRPC:从通信模式到场景选型
  • uniapp使用uni-ui怎么修改默认的css样式比如多选框及样式覆盖小程序/安卓/ios兼容问题
  • taro微信小程序的tsconfig.json文件说明
  • Hyperledger Fabric V2.5 生产环境部署及安装Java智能合约
  • 从env到mm_struct:环境变量与虚拟内存的底层实现
  • 来伊份养馋记社区零售 4.0 上海首店落沪:重构 “家门口” 的生活服务生态
  • Django实战:基于Django和openpyxl实现Excel导入导出功能
  • AWS IoT Core CloudWatch监控完整指南