创建文件的目录结构
利用这个界面创建
序号 | 名称 | 说明 |
1 | SEMS | maven父工程,统一管理依赖版本,聚合其他子模块 |
2 | sems-common | 子模块,存放公共类,例如:工具类、常量类、异常类等 |
3 | sems-pojo | 子模块,存放实体类、VO、DTO等 |
4 | sems-server | 子模块,后端服务,存放配置文件、Controller、Service、Mapper等 |
配置pom文件
文件路径:SEMS\pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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>2.7.3</version>
</parent>
<groupId>com.ljc</groupId>
<artifactId>SEMS</artifactId>
<packaging>pom</packaging>
<version>0.0.1-SNAPSHOT</version>
<modules>
<module>sems-common</module>
<module>sems-pojo</module>
<module>sems-server</module>
</modules>
<!--它定义了一系列项目依赖的版本号-->
<properties>
<mybatis.spring>2.2.0</mybatis.spring>
<lombok>1.18.20</lombok>
<fastjson>1.2.76</fastjson>
<commons.lang>2.6</commons.lang>
<druid>1.2.1</druid>
<pagehelper>1.3.0</pagehelper>
<aliyun.sdk.oss>3.10.2</aliyun.sdk.oss>
<knife4j>3.0.2</knife4j>
<aspectj>1.9.4</aspectj>
<jjwt>0.9.1</jjwt>
<jaxb-api>2.3.1</jaxb-api>
<poi>3.16</poi>
</properties>
<dependencyManagement>
<dependencies>
<!--mybatis的依赖-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis.spring}</version>
</dependency>
<!--Lombok 是一个 Java 库,用于自动化生成如 getter、setter、toString 等方法-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok}</version>
</dependency>
<!--一个 Java 语言编写的高性能功能完善的 JSON 库-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson}</version>
</dependency>
<!--一个包含大量静态方法的库,用于处理 Java 核心 API 中没有的常用功能-->
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>${commons.lang}</version>
</dependency>
<!--阿里巴巴开源的数据库连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>${druid}</version>
</dependency>
<!--MyBatis 分页插件-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>${pagehelper}</version>
</dependency>
<!--Swagger 的增强 UI 实现,用于 API 文档生成-->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>${knife4j}</version>
</dependency>
<!--AspectJ 是一个面向切面的编程(AOP)框架-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>${aspectj}</version>
</dependency>
<!--Java JWT(JSON Web Tokens)库,用于生成和解析 JWT 令牌-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>${jjwt}</version>
</dependency>
<!--阿里云对象存储服务(OSS)的 Java SDK-->
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>${aliyun.sdk.oss}</version>
</dependency>
<!--用于将 Java 类映射到 XML 表示-->
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>${jaxb-api}</version>
</dependency>
<!-- poi -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>${poi}</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>${poi}</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
SEMS\sems-server\pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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>com.ljc</groupId>
<artifactId>SEMS</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>sems-server</artifactId>
<dependencies>
<dependency>
<groupId>com.ljc</groupId>
<artifactId>sems-common</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.ljc</groupId>
<artifactId>sems-pojo</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
</dependency>
<!-- poi -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
SEMS\sems-pojo\pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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>com.ljc</groupId>
<artifactId>SEMS</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>sems-pojo</artifactId>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
</dependency>
</dependencies>
</project>
SEMS\sems-common\pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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>com.ljc</groupId>
<artifactId>SEMS</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>sems-common</artifactId>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
</dependency>
<!--支持配置属性类,yml文件中可以提示配置项-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
</dependency>
<!--微信支付-->
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-apache-httpclient</artifactId>
<version>0.4.9</version>
</dependency>
</dependencies>
</project>
配置好后需要点击这个编译一下,显示成功才可继续后面的
配置git环境上传到仓库,反之代码丢失
在各自对应的结构下构建相应的目录结构
名称 | 说明 |
constant | 存放相关常量类 |
context | 存放上下文类 |
enumeration | 项目的枚举类存储 |
exception | 存放自定义异常类 |
json | 处理json转换的类 |
properties | 存放SpringBoot相关的配置属性类 |
result | 返回结果类的封装 |
utils | 常用工具类 |
名称 | 说明 |
Entity | 实体,通常和数据库中的表对应 |
DTO | 数据传输对象,通常用于程序中各层之间传递数据 |
VO | 视图对象,为前端展示数据提供的对象 |
POJO | 普通Java对象,只有属性和对应的getter和setter |
名称 | 说明 |
config | 存放配置类 |
controller | 存放controller类 |
interceptor | 存放拦截器类 |
mapper | 存放mapper接口 |
service | 存放service类 |
SemsApplication | 启动类 |
配置application.yml文件
sems-server/src/main/resources/application.yml
server:
port: 8080
spring:
profiles:
active: dev
main:
allow-circular-references: true
datasource:
druid:
driver-class-name: ${ljc.datasource.driver-class-name}
url: jdbc:mysql://${ljc.datasource.host}:${ljc.datasource.port}/${ljc.datasource.database}?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true
username: ${ljc.datasource.username}
password: ${ljc.datasource.password}
mybatis:
#mapper配置文件
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.ljc.entity
configuration:
#开启驼峰命名
map-underscore-to-camel-case: true
logging:
level:
com:
sky:
mapper: debug
service: info
controller: info
ljc:
jwt:
# 设置jwt签名加密时使用的秘钥
admin-secret-key: kussm
# 设置jwt过期时间
admin-ttl: 7200000
# 设置前端传递过来的令牌名称
admin-token-name: token
sems-server/src/main/resources/application-dev.yml
ljc:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
host: localhost
port: 3306
database: sems
username: root
password: 8888
编写一些基础代码
1. 全局异常处理器,处理项目中抛出的业务异常
GlobalExceptionHandler(全局异常处理器)
sems-server\src\main\java\com\ljc\handler\GlobalExceptionHandler.java
package com.ljc.handler;
import com.ljc.exception.BaseException;
import com.ljc.result.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
/**
* 全局异常处理器,处理项目中抛出的业务异常
*/
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
/**
* 捕获业务异常
* @param ex
* @return
*/
@ExceptionHandler
public Result exceptionHandler(BaseException ex){
log.error("异常信息:{}", ex.getMessage());
return Result.error(ex.getMessage());
}
}
同时创建相关代码
BaseException(业务异常)
sems-common\src\main\java\com\ljc\exception\BaseException.java
package com.ljc.exception;
/**
* 业务异常
*/
public class BaseException extends RuntimeException {
public BaseException() {
}
public BaseException(String msg) {
super(msg);
}
}
Result(后端统一返回结果)
sems-common\src\main\java\com\ljc\result\Result.java
package com.ljc.result;
import lombok.Data;
import java.io.Serializable;
/**
* 后端统一返回结果
* @param <T>
*/
@Data
public class Result<T> implements Serializable {
private Integer code; //编码:1成功,0和其它数字为失败
private String msg; //错误信息
private T data; //数据
public static <T> Result<T> success() {
Result<T> result = new Result<T>();
result.code = 1;
return result;
}
public static <T> Result<T> success(T object) {
Result<T> result = new Result<T>();
result.data = object;
result.code = 1;
return result;
}
public static <T> Result<T> error(String msg) {
Result result = new Result();
result.msg = msg;
result.code = 0;
return result;
}
}
2. 配置类,注册web层相关组件JWT
注册自定义拦截器jwt
通过knife4j生成接口文档
设置静态资源映射
WebMvcConfiguration(配置类,注册web层相关组件)
sems-server\src\main\java\com\ljc\config\WebMvcConfiguration.java
package com.ljc.config;
import com.ljc.interceptor.JwtTokenAdminInterceptor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
/**
* 配置类,注册web层相关组件
*/
@Configuration
@Slf4j
public class WebMvcConfiguration extends WebMvcConfigurationSupport {
@Autowired
private JwtTokenAdminInterceptor jwtTokenAdminInterceptor;
/**
* 注册自定义拦截器
*
* @param registry
*/
protected void addInterceptors(InterceptorRegistry registry) {
log.info("开始注册自定义拦截器...");
registry.addInterceptor(jwtTokenAdminInterceptor)
.addPathPatterns("/admin/**")
.excludePathPatterns("/admin/student/login");
}
/**
* 通过knife4j生成接口文档
* @return
*/
@Bean
public Docket docket() {
ApiInfo apiInfo = new ApiInfoBuilder()
.title("体育器材项目接口文档")
.version("1.0")
.description("体育器材项目接口文档")
.build();
Docket docket = new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo)
.select()
.apis(RequestHandlerSelectors.basePackage("com.ljc.controller"))
.paths(PathSelectors.any())
.build();
return docket;
}
/**
* 设置静态资源映射
* @param registry
*/
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/doc.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
}
}
3. 相关联的jwt令牌校验的拦截器
JwtTokenAdminInterceptor
sems-server\src\main\java\com\ljc\interceptor\JwtTokenAdminInterceptor.java
package com.ljc.interceptor;
import com.ljc.constant.JwtClaimsConstant;
import com.ljc.properties.JwtProperties;
import com.ljc.utils.JwtUtil;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* jwt令牌校验的拦截器
*/
@Component
@Slf4j
public class JwtTokenAdminInterceptor implements HandlerInterceptor {
@Autowired
private JwtProperties jwtProperties;
/**
* 校验jwt
*
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//判断当前拦截到的是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);
//3、通过,放行
return true;
} catch (Exception ex) {
//4、不通过,响应401状态码
response.setStatus(401);
return false;
}
}
}
4. 常量类(后面用)
JwtClaimsConstant
sems-common/src/main/java/com/ljc/constant/JwtClaimsConstant.java
package com.ljc.constant;
public class JwtClaimsConstant {
public static final String EMP_ID = "empId";
public static final String USER_ID = "userId";
public static final String PHONE = "phone";
public static final String USERNAME = "username";
public static final String NAME = "name";
}
MessageConstant
sems-common/src/main/java/com/ljc/constant/MessageConstant.java
package com.ljc.constant;
/**
* 信息提示常量类
*/
public class MessageConstant {
public static final String PASSWORD_ERROR = "密码错误";
public static final String ACCOUNT_NOT_FOUND = "账号不存在";
public static final String ACCOUNT_LOCKED = "账号被锁定";
public static final String UNKNOWN_ERROR = "未知错误";
public static final String USER_NOT_LOGIN = "用户未登录";
public static final String CATEGORY_BE_RELATED_BY_SETMEAL = "当前分类关联了套餐,不能删除";
public static final String CATEGORY_BE_RELATED_BY_DISH = "当前分类关联了菜品,不能删除";
public static final String SHOPPING_CART_IS_NULL = "购物车数据为空,不能下单";
public static final String ADDRESS_BOOK_IS_NULL = "用户地址为空,不能下单";
public static final String LOGIN_FAILED = "登录失败";
public static final String UPLOAD_FAILED = "文件上传失败";
public static final String SETMEAL_ENABLE_FAILED = "套餐内包含未启售菜品,无法启售";
public static final String PASSWORD_EDIT_FAILED = "密码修改失败";
public static final String DISH_ON_SALE = "起售中的菜品不能删除";
public static final String SETMEAL_ON_SALE = "起售中的套餐不能删除";
public static final String DISH_BE_RELATED_BY_SETMEAL = "当前菜品关联了套餐,不能删除";
public static final String ORDER_STATUS_ERROR = "订单状态错误";
public static final String ORDER_NOT_FOUND = "订单不存在";
}
StatusConstant
sems-common/src/main/java/com/ljc/constant/StatusConstant.java
package com.ljc.constant;
/**
* 状态常量,启用或者禁用
*/
public class StatusConstant {
//启用
public static final Integer ENABLE = 1;
//禁用
public static final Integer DISABLE = 0;
}
5. 异常类
AccountLockedException
sems-common/src/main/java/com/ljc/exception/AccountLockedException.java
package com.ljc.exception;
/**
* 账号被锁定异常
*/
public class AccountLockedException extends BaseException {
public AccountLockedException() {
}
public AccountLockedException(String msg) {
super(msg);
}
}
AccountNotFoundException
sems-common/src/main/java/com/ljc/exception/AccountNotFoundException.java
package com.ljc.exception;
/**
* 账号不存在异常
*/
public class AccountNotFoundException extends BaseException {
public AccountNotFoundException() {
}
public AccountNotFoundException(String msg) {
super(msg);
}
}
PasswordErrorException
sems-common/src/main/java/com/ljc/exception/PasswordErrorException.java
package com.ljc.exception;
/**
* 密码错误异常
*/
public class PasswordErrorException extends BaseException {
public PasswordErrorException() {
}
public PasswordErrorException(String msg) {
super(msg);
}
}
6. 配置类
JwtProperties
sems-common/src/main/java/com/ljc/properties/JwtProperties.java
package com.ljc.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix = "ljc.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;
}
7. 实用工具
JwtUtil(生成jwt)
sems-common/src/main/java/com/ljc/utils/JwtUtil.java
package com.ljc.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;
}
}
8. 阶段效果展示
现在就可以开始运行代码了
SemsApplication
sems-server/src/main/java/com/ljc/SemsApplication.java
package com.ljc;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@SpringBootApplication
@EnableTransactionManagement //开启注解方式的事务管理
@Slf4j
public class SemsApplication {
public static void main(String[] args) {
SpringApplication.run(SemsApplication.class, args);
log.info("server started");
}
}
打开浏览器输入:http://localhost:8080/doc.html
可以看到通过knife4j生成的初步接口文档,后面慢慢完善接口
学生管理--登录
接口设计
请求路径:/admin/student/login
请求方式:POST
设计数据库
student表为学生表,用于存储学生信息。具体表结构如下:
字段名 | 数据类型 | 说明 | 备注 |
id | bigint | 主键 | 自增 |
name | varchar(32) | 姓名 | |
username | varchar(32) | 用户名 | 唯一 |
password | varchar(64) | 密码 | |
phone | varchar(11) | 手机号 | |
sex | varchar(2) | 性别 | |
id_number | varchar(18) | 身份证号 | |
status | int | 账号状态 | 1正常 0锁定 |
create_time | datetime | 创建时间 | |
update_time | datetime | 最后修改时间 | |
create_user | bigint | 创建人id | |
update_user | bigint | 最后修改人id |
sql语句
DROP TABLE IF EXISTS student;
CREATE TABLE `employee` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
`name` varchar(32) COLLATE utf8_bin NOT NULL COMMENT '姓名',
`username` varchar(32) COLLATE utf8_bin NOT NULL COMMENT '用户名',
`password` varchar(64) COLLATE utf8_bin NOT NULL COMMENT '密码',
`phone` varchar(11) COLLATE utf8_bin NOT NULL COMMENT '手机号',
`sex` varchar(2) COLLATE utf8_bin NOT NULL COMMENT '性别',
`id_number` varchar(18) COLLATE utf8_bin NOT NULL COMMENT '身份证号',
`status` int NOT NULL DEFAULT '1' COMMENT '状态 0:禁用,1:启用',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`create_user` bigint DEFAULT NULL COMMENT '创建人',
`update_user` bigint DEFAULT NULL COMMENT '修改人',
PRIMARY KEY (`id`),
UNIQUE KEY `idx_username` (`username`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_bin COMMENT='员工信息';
INSERT INTO student VALUES (1,'管理员','admin','123456','13812312312','1','110101199001010047',1,'2022-02-15 15:51:20','2022-02-17 09:16:20',10,1);
设计接收和回传数据的DTO和VO结构以及和数据库对应的entity实体类
Student
sems-pojo/src/main/java/com/ljc/entity/Student.java
package com.ljc.entity;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.time.LocalDateTime;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Student implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
private String username;
private String name;
private String password;
private String phone;
private String sex;
private String idNumber;
private Integer status;
//@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;
//@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime updateTime;
private Long createUser;
private Long updateUser;
}
StudentLoginDTO
sems-pojo/src/main/java/com/ljc/dto/StudentLoginDTO.java
package com.ljc.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
@Data
@ApiModel(description = "学生登录时传递的数据模型")
public class StudentLoginDTO implements Serializable {
@ApiModelProperty("用户名")
private String username;
@ApiModelProperty("密码")
private String password;
}
StudentLoginVO
sems-pojo/src/main/java/com/ljc/vo/StudentLoginVO.java
package com.ljc.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@ApiModel(description = "学生登录返回的数据格式")
public class StudentLoginVO implements Serializable {
@ApiModelProperty("主键值")
private Long id;
@ApiModelProperty("用户名")
private String userName;
@ApiModelProperty("姓名")
private String name;
@ApiModelProperty("jwt令牌")
private String token;
}
设计真正业务的代码
controller层-StudentController
sems-server/src/main/java/com/ljc/controller/student/StudentController.java
package com.ljc.controller.student;
import com.ljc.constant.JwtClaimsConstant;
import com.ljc.dto.StudentLoginDTO;
import com.ljc.entity.Student;
import com.ljc.properties.JwtProperties;
import com.ljc.result.Result;
import com.ljc.service.StudentService;
import com.ljc.utils.JwtUtil;
import com.ljc.vo.StudentLoginVO;
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;
@RestController
@RequestMapping("/admin/student")
@Slf4j
public class StudentController {
@Autowired
private StudentService studentService;
@Autowired
private JwtProperties jwtProperties;
/**
* 登录
*
* @param studentLoginDTO
* @return
*/
@PostMapping("/login")
public Result<StudentLoginVO> login(@RequestBody StudentLoginDTO studentLoginDTO) {
log.info("学生登录:{}", studentLoginDTO);
Student student = studentService.login(studentLoginDTO);
//登录成功后,生成jwt令牌
Map<String, Object> claims = new HashMap<>();
claims.put(JwtClaimsConstant.EMP_ID, student.getId());
String token = JwtUtil.createJWT(
jwtProperties.getAdminSecretKey(),
jwtProperties.getAdminTtl(),
claims);
StudentLoginVO studentLoginVO = StudentLoginVO.builder()
.id(student.getId())
.userName(student.getUsername())
.name(student.getName())
.token(token)
.build();
return Result.success(studentLoginVO);
}
/**
* 退出
*
* @return
*/
@PostMapping("/logout")
public Result<String> logout() {
return Result.success();
}
}
service层-StudentService
sems-server/src/main/java/com/ljc/service/StudentService.java
package com.ljc.service;
import com.ljc.dto.StudentLoginDTO;
import com.ljc.entity.Student;
public interface StudentService {
/**
* 学生登录
* @param studentLoginDTO
* @return
*/
Student login(StudentLoginDTO studentLoginDTO);
}
service层-StudentServiceImpl
sems-server/src/main/java/com/ljc/service/impl/StudentServiceImpl.java
package com.ljc.service.impl;
import com.ljc.constant.MessageConstant;
import com.ljc.constant.StatusConstant;
import com.ljc.dto.StudentLoginDTO;
import com.ljc.entity.Student;
import com.ljc.exception.AccountLockedException;
import com.ljc.exception.AccountNotFoundException;
import com.ljc.exception.PasswordErrorException;
import com.ljc.mapper.StudentMapper;
import com.ljc.service.StudentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class StudentServiceImpl implements StudentService {
@Autowired
private StudentMapper studentMapper;
/**
* 学生登录
*
* @param studentLoginDTO
* @return
*/
public Student login(StudentLoginDTO studentLoginDTO) {
String username = studentLoginDTO.getUsername();
String password = studentLoginDTO.getPassword();
//1、根据用户名查询数据库中的数据
Student student = studentMapper.getByUsername(username);
//2、处理各种异常情况(用户名不存在、密码不对、账号被锁定)
if (student == null) {
//账号不存在
throw new AccountNotFoundException(MessageConstant.ACCOUNT_NOT_FOUND);
}
//密码比对
// TODO 后期需要进行md5加密,然后再进行比对
if (!password.equals(student.getPassword())) {
//密码错误
throw new PasswordErrorException(MessageConstant.PASSWORD_ERROR);
}
if (student.getStatus() == StatusConstant.DISABLE) {
//账号被锁定
throw new AccountLockedException(MessageConstant.ACCOUNT_LOCKED);
}
//3、返回实体对象
return student;
}
}
mapper层-StudentMapper
sems-server/src/main/java/com/ljc/mapper/StudentMapper.java
package com.ljc.mapper;
import com.ljc.entity.Student;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
@Mapper
public interface StudentMapper {
/**
* 根据用户名查询学生
* @param username
* @return
*/
@Select("select * from student where username = #{username}")
Student getByUsername(String username);
}
阶段结果展示
通过swagger测试
注意:我在运行中出现的一些问题:
基本就没什么问题了。
这样登录功能就搞定了!!!!!
目前为止整体结构
完善登录功能
学生表中的密码是明文存储,安全性太低
使用MD5加密方式对明文密码加密
”123456“进行MD5加密后结果为:
e10adc3949ba59abbe56e057f20f883e
进入数据库,修改密码为这个
修改代码
完整代码:
sems-server/src/main/java/com/ljc/service/impl/StudentServiceImpl.java
package com.ljc.service.impl;
import com.ljc.constant.MessageConstant;
import com.ljc.constant.StatusConstant;
import com.ljc.dto.StudentLoginDTO;
import com.ljc.entity.Student;
import com.ljc.exception.AccountLockedException;
import com.ljc.exception.AccountNotFoundException;
import com.ljc.exception.PasswordErrorException;
import com.ljc.mapper.StudentMapper;
import com.ljc.service.StudentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;
@Service
public class StudentServiceImpl implements StudentService {
@Autowired
private StudentMapper studentMapper;
/**
* 学生登录
*
* @param studentLoginDTO
* @return
*/
public Student login(StudentLoginDTO studentLoginDTO) {
String username = studentLoginDTO.getUsername();
String password = studentLoginDTO.getPassword();
//1、根据用户名查询数据库中的数据
Student student = studentMapper.getByUsername(username);
//2、处理各种异常情况(用户名不存在、密码不对、账号被锁定)
if (student == null) {
//账号不存在
throw new AccountNotFoundException(MessageConstant.ACCOUNT_NOT_FOUND);
}
//密码比对
// 进行md5加密,然后再进行比对
password = DigestUtils.md5DigestAsHex(password.getBytes());
if (!password.equals(student.getPassword())) {
//密码错误
throw new PasswordErrorException(MessageConstant.PASSWORD_ERROR);
}
if (student.getStatus() == StatusConstant.DISABLE) {
//账号被锁定
throw new AccountLockedException(MessageConstant.ACCOUNT_LOCKED);
}
//3、返回实体对象
return student;
}
}
初步完成这样