目录
前言
1.SpringBoot项目的创建
2.相关技术
3.项目架构
4.项目关键代码
5.项目最终的运行效果
编辑
6.PostMan测试接口结果
前言
学习了SpringBoot之后,才觉得SpringBoot真的很方便,相比传统的SSH,SSM,SpringBoot无疑是更轻量级的框架开发,约定大于配置的思想,使得项目的搭建更加简单。
1.SpringBoot项目的创建
可以通过https://start.spring.io/ ,这个网址创建一个简单的SpringBoot项目,然后修改成适配当前编译器的SpringBoot版本。
2.相关技术
SpringBoot:项目的核心框架
MybatisPlus:一个半自动的ORM框架,用于操作数据库的组件
JWT:用于登录身份校验
MYSQL:用于存储业务数据
3.项目架构
common层:用于存放公共的方法,以及公共类,公共实体
config层:存放相应的配置类,如:MybatisPlus配置类,SpringMVC的配置类
controller层:存放给前端提供接口请求的类,也称之为视图层。
entity层:一般存放与数据库关联的实体类,属性和数据库的字段相对相应。
interpretor层:存放拦截器,如token拦截
mapper层:存放抽象的数据库接口,如Mybatis的一些接口映射,以及方法映射。
dao层:数据库访问层
service层:业务逻辑层,主要存放业务逻辑代码,供controller层调用。
配置文件 pom.xml:存放Java类库的依赖,如SpringBoot起步依赖,Mysql类库依赖、MybatisPlus类库依赖
4.项目关键代码
JWT组件: 用于生成token和解析token的组件
package com.example.harmonybackend.utils;import io.jsonwebtoken.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;import java.util.Date;
import java.util.Map;
import java.util.UUID;/*** token生成组件类*/
@Component
@Slf4j
public class JWTUtils {private static String secret = "123456";public static String getToken(Map<String,Object> claims){// JWT的签发时间long nowMillis = System.currentTimeMillis();Date now = new Date(nowMillis);// 指定签名的时候使用的签名算法SignatureAlgorithm signatureAlgotithm = SignatureAlgorithm.HS256;long expMillis = nowMillis + 8*60L; // 默认设置8小时过期Date expirationDate = new Date(expMillis);String token = Jwts.builder() // 创建jwt builder.setClaims(claims) // 必须放最前面,不然后面设置的东西都会没有:如setExpiration会没有时间.setId(UUID.randomUUID().toString()) // jwt唯一标识.setIssuedAt(now) // 签发时间.setExpiration(expirationDate) // 过期时间.signWith(signatureAlgotithm, secret) // 设置签名实用的签名算法和使用的密钥.compact();return token;}/*** 解析token的方法* @param token* @return*/public static Claims parseToken(String token){String msg = null;try{Claims claims = Jwts.parser().setAllowedClockSkewSeconds(480) // 允许8小时的偏移.setSigningKey(secret) // 设置签名密钥.parseClaimsJws(token).getBody(); // 设置需要解析的JWTreturn claims;}catch (SignatureException se) {msg = "密钥错误";log.error(msg, se);throw new RuntimeException(msg);}catch (MalformedJwtException me) {msg = "密钥算法或者密钥转换错误";log.error(msg, me);throw new RuntimeException(msg);}catch (MissingClaimException mce) {msg = "密钥缺少校验数据";log.error(msg, mce);throw new RuntimeException(msg);}catch (ExpiredJwtException mce) {msg = "密钥已过期";log.error(msg, mce);throw new RuntimeException(msg);}catch (JwtException jwte) {msg = "密钥解析错误";log.error(msg, jwte);throw new RuntimeException(msg);}}}
AES加密解密组件:用于登录密码的加密与解密
package com.example.harmonybackend.utils;import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;public class AesEncryption {private static final String ALGORITHM = "AES";private static final int KEY_SIZE = 128; // 或者 192, 256public static SecretKey generateKey() throws Exception {KeyGenerator keyGen = KeyGenerator.getInstance(ALGORITHM);keyGen.init(KEY_SIZE);return keyGen.generateKey();}public static String encrypt(String plainText, SecretKey secretKey) throws Exception {Cipher cipher = Cipher.getInstance(ALGORITHM);cipher.init(Cipher.ENCRYPT_MODE, secretKey);byte[] encryptedBytes = cipher.doFinal(plainText.getBytes());return Base64.getEncoder().encodeToString(encryptedBytes);}public static String decrypt(String encryptedText, SecretKey secretKey) throws Exception {Cipher cipher = Cipher.getInstance(ALGORITHM);cipher.init(Cipher.DECRYPT_MODE, secretKey);byte[] decodedBytes = Base64.getDecoder().decode(encryptedText);byte[] decryptedBytes = cipher.doFinal(decodedBytes);return new String(decryptedBytes);}public static void main(String args[]) throws Exception {// 仅需执行一次以生成并保存密钥/* SecretKey secretKey = AesEncryption.generateKey();byte[] encodedKey = secretKey.getEncoded();String base64EncodedKey11 = Base64.getEncoder().encodeToString(encodedKey);System.out.println("base64EncodedKey11=="+base64EncodedKey11);*/// 假设你已经将密钥保存为Base64字符串形式在某个地方String base64EncodedKey = "SGRieOb4PY1HbSXOnmQohw==";byte[] decodedKey = Base64.getDecoder().decode(base64EncodedKey);SecretKey originalKey = new SecretKeySpec(decodedKey, 0, decodedKey.length, "AES");String encryptPass = encrypt("admin",originalKey);System.out.println(encryptPass);System.out.println("解密:"+ decrypt(encryptPass,originalKey));}
}
拦截器类JwtInterpretor:用于Token的拦截
package com.example.harmonybackend.interpretor;import com.example.harmonybackend.utils.JWTUtils;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.jsonwebtoken.Claims;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** SpringMVC拦截器*/
public class JwtInterpretor implements HandlerInterceptor {private final List<String> excludePaths = Arrays.asList("/user/login", "/public/**");@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String uri = request.getRequestURI();for (String path : excludePaths) {if (new AntPathMatcher().match(path, uri)) {return true;}}Map<String, Object> map = new HashMap<>();// 获取请求头中令牌String token = request.getHeader("login-token");if(!StringUtils.isEmpty(token)) {try {Claims claims = JWTUtils.parseToken(token); // 如果找到令牌就使用 JWTUtils.parseJwt() 方法解析令牌return true; // 解析成功,即令牌有效,返回true} catch (RuntimeException e) { // 如果解析失败,会捕获 RuntimeException 异常e.printStackTrace();map.put("msg", e.getMessage()); // map.put("msg","密钥错误!");}}map.put("message","token为null,必须携带token");map.put("state",false); // 设置状态// 将map 转为 jsonString json = new ObjectMapper().writeValueAsString(map);response.setContentType("application/json;charset=UTF-8");response.getWriter().println(json);return false;}
}
SpringMVC的配置: 基于方法的拦截器,通过拦截控制接口的访问。
package com.example.harmonybackend.interpretor;import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;/*** mvc拦截器*/
@Configuration
public class MVCConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {String[] patterns = new String[] {"/user/login/**","/user/register/**","/*.html","/css/**","/js/**","/images/**","/layui/**"}; // 添加不拦截的方法//添加jwt拦截器registry.addInterceptor(new JwtInterpretor()).excludePathPatterns(patterns)// 其他接口token验证;.addPathPatterns("/**").order(0); // 不进行token验证}}
注册和登录的业务逻辑类UserService:用于登录验证,以及注册时,密码的加密解密
package com.example.harmonybackend.service.impl;import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.harmonybackend.common.ApiResponse;
import com.example.harmonybackend.config.PasswordConfig;
import com.example.harmonybackend.entity.UserEntity;
import com.example.harmonybackend.mapper.UserMapper;
import com.example.harmonybackend.myemnu.ApiError;
import com.example.harmonybackend.request.RegisterRequstParam;
import com.example.harmonybackend.request.UserRequestParam;
import com.example.harmonybackend.service.IUserService;
import com.example.harmonybackend.utils.AesEncryption;
import com.example.harmonybackend.utils.JWTUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;import javax.annotation.Resource;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
import java.util.HashMap;
import java.util.List;
import java.util.Map;@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, UserEntity> implements IUserService {@Resourceprivate PasswordConfig passwordConfig;@Overridepublic ApiResponse login(UserRequestParam requestParam) {QueryWrapper<UserEntity> param = new QueryWrapper<>();ApiResponse apiResponse = new ApiResponse();byte[] decodedKey = Base64.getDecoder().decode(passwordConfig.getBase64EncodedKey());SecretKey originalKey = new SecretKeySpec(decodedKey, 0, decodedKey.length, "AES");try {String encryptPass = AesEncryption.encrypt(requestParam.getPassword(),originalKey);param.eq("username",requestParam.getUsername());param.eq("password",encryptPass);UserEntity userEntity = getOne(param);if (null == userEntity){apiResponse.setCode(ApiError.INTERNAL_ERROR.getCode());apiResponse.setMessage("账号不存在或密码错误");return apiResponse;}}catch (Exception e){e.printStackTrace();apiResponse.setCode(ApiError.INTERNAL_ERROR.getCode());apiResponse.setMessage("账号不存在或密码错误");return apiResponse;}apiResponse.setCode(ApiError.SUCCESS.getCode());apiResponse.setMessage(ApiError.SUCCESS.getMessage());Map<String,Object>resMap = new HashMap<>();resMap.put("username",requestParam.getUsername());String login_token = JWTUtils.getToken(resMap);Map<String,Object>dataMap = new HashMap<>();dataMap.put("login-token",login_token);apiResponse.setData(dataMap);return apiResponse;}@Overridepublic ApiResponse register(RegisterRequstParam registerRequstParam) {UserEntity entity = new UserEntity();ApiResponse apiResponse = new ApiResponse();byte[] decodedKey = Base64.getDecoder().decode(passwordConfig.getBase64EncodedKey());SecretKey originalKey = new SecretKeySpec(decodedKey, 0, decodedKey.length, "AES");try {QueryWrapper<UserEntity>queryMap = new QueryWrapper<>();queryMap.eq("username",registerRequstParam.getUsername());UserEntity existUser = getOne(queryMap);if (null!=existUser){apiResponse.setCode(ApiError.INTERNAL_ERROR.getCode());apiResponse.setMessage("用户已存在,请勿重复注册!");return apiResponse;}String encryptPass = AesEncryption.encrypt(registerRequstParam.getPassword(),originalKey);registerRequstParam.setPassword(encryptPass);//对象拷贝BeanUtils.copyProperties(registerRequstParam ,entity);//保存数据save(entity);}catch (Exception e){e.printStackTrace();apiResponse.setCode(ApiError.INTERNAL_ERROR.getCode());apiResponse.setMessage("注册失败!");return apiResponse;}apiResponse.setCode(ApiError.SUCCESS.getCode());apiResponse.setMessage(ApiError.SUCCESS.getMessage());return apiResponse;}@Overridepublic ApiResponse<List<UserEntity>> findAll() {List<UserEntity> userEntityList= list();ApiResponse<List<UserEntity>>apiResponse = new ApiResponse<>();apiResponse.setCode(ApiError.SUCCESS.getCode());apiResponse.setMessage(ApiError.SUCCESS.getMessage());apiResponse.setData(userEntityList);return apiResponse;}}
实体类
package com.example.harmonybackend.entity;import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;@Data
@TableName("sys_user")
public class UserEntity {@TableId(type = IdType.ASSIGN_ID)private Long id;//用户名private String username;//密码private String password;//岁数private Integer age;//邮箱private String email;//性别 0女 1 男private Integer gender;//真实姓名private String realName;@TableLogicprivate Integer deleted; // 逻辑删除字段}
Mysql数据库脚本
/*Navicat Premium Data TransferSource Server : localhost_3306Source Server Type : MySQLSource Server Version : 80037Source Host : localhost:3306Source Schema : harmonydbTarget Server Type : MySQLTarget Server Version : 80037File Encoding : 65001Date: 27/12/2024 11:37:05
*/SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;-- ----------------------------
-- Table structure for sys_user
-- ----------------------------
DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user` (`id` bigint(0) NOT NULL COMMENT 'id',`username` varchar(18) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '用户名',`password` varchar(32) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '密码',`age` int(0) NULL DEFAULT NULL COMMENT '年龄',`email` varchar(32) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '邮箱',`deleted` int(0) NULL DEFAULT 0 COMMENT '逻辑删除',`gender` int(0) NULL DEFAULT NULL COMMENT '性别(0女,1男)',`real_name` varchar(10) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci ROW_FORMAT = Dynamic;-- ----------------------------
-- Records of sys_user
-- ----------------------------
INSERT INTO `sys_user` VALUES (1235653235656, 'admin', 'Y8itXSytDD76om64lGZ1Wg==', 18, '123456789@qq.com', 0, NULL, NULL);
INSERT INTO `sys_user` VALUES (1872205908998721538, 'zhangsan', 'xoixyTmaBgbAwb4Ly2VnxQ==', 18, '1255656@qq.com', 0, NULL, '张三');
INSERT INTO `sys_user` VALUES (1872219115704414209, 'lisi', 'xoixyTmaBgbAwb4Ly2VnxQ==', 18, '1255656@qq.com', 0, NULL, '张三');
INSERT INTO `sys_user` VALUES (1872219905814818818, 'wangwu', 'xoixyTmaBgbAwb4Ly2VnxQ==', 18, '1255656@qq.com', 0, 0, '王五');
INSERT INTO `sys_user` VALUES (1872220108869464066, 'laoliu', 'SYhcFqjbKKqeJbC0qO5i0Q==', 18, '1255656@qq.com', 0, 0, '老六');SET FOREIGN_KEY_CHECKS = 1;
5.项目最终的运行效果
6.PostMan测试接口结果
博文代码只有关键部分,如有需要,请前往下载完整版。此博文有助于初学者学习SpringBoot,你们的点赞和收藏是我前进的动力。