文章
springboot + mybatis plus 示例
需求jdk 17,Ideaj插件:maven-search、MybatisX
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>3.0.5</version>
</parent>
<groupId>com.learn</groupId>
<artifactId>springboot-headline-part</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.27</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.28</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>MybatisX插件根据数据库表生成:Mapper、Pojo、Service
resources/application.yml
server:
port: 8080
servlet:
context-path: /
# 连接配置
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://xxx:3306/datacenter
username: xxx
# 纯数字需要两侧添加`12345`
password: xxx
driver-class-name: com.mysql.cj.jdbc.Driver
druid:
initial-size: 5
min-idle: 5
max-active: 20
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
type-aliases-package: com.learn.pojo
global-config:
db-config:
logic-delete-field: isDeleted # 全局逻辑删除实体属性名
logic-delete-value: 1 # 逻辑删除
logic-not-delete-value: 0 # 逻辑未删除
id-type: auto # 主键策略自增长
table-prefix: news_ # 设置表的前缀
jwt:
token:
tokenExpiration: 120 # 有效时间 分钟
tokenSignKey: headline123456 # 当前程序签名密钥 自定义resources/com/learn/mapper
HeadlineMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.learn.mapper.HeadlineMapper">
<resultMap id="BaseResultMap" type="com.learn.pojo.Headline">
<id property="hid" column="hid"/>
<result property="title" column="title"/>
<result property="article" column="article"/>
<result property="type" column="type"/>
<result property="publisher" column="publisher"/>
<result property="pageViews" column="page_views"/>
<result property="createTime" column="create_time"/>
<result property="updateTime" column="update_time"/>
<result property="version" column="version"/>
<result property="isDeleted" column="is_deleted"/>
</resultMap>
<sql id="Base_Column_List">
hid
,title,article,type,publisher,page_views,
create_time,update_time,version,is_deleted
</sql>
<!-- IPage<Map> selectMyPage(IPage page, @Param("portalVo") PortalVo portalVo);-->
<select id="selectMyPage" resultType="map">
SELECT hid,
title,
page_views pageViews,
TIMESTAMPDIFF(HOUR, create_time, NOW()) pastHours,
publisher
FROM news_headline
WHERE is_deleted = 0
<if test="portalVo.keyWords != null and portalVo.keywords.length() > 0">
AND title LIKE concat('%', #{portalVo.keyWords}, '%')
</if>
<if test="portalVo.type != 0">
AND type = #{portalVo.type}
</if>
</select>
<!-- Map queryDetailMap(Integer hid);-->
<select id="queryDetailMap" resultType="map">
SELECT hid,
title,
article,
h.version,
tname as typeName,
page_views as pageViews,
TIMESTAMPDIFF(HOUR, create_time, now()) as pastHours,
publisher,
nick_name as author
FROM news_headline h
LEFT JOIN news_type t ON h.type = t.tid
LEFT JOIN news_user u ON h.publisher = u.uid
WHERE hid = #{hid}
</select>
</mapper>
TypeMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.learn.mapper.TypeMapper">
<resultMap id="BaseResultMap" type="com.learn.pojo.Type">
<id property="tid" column="tid" />
<result property="tname" column="tname" />
<result property="version" column="version" />
<result property="isDeleted" column="is_deleted" />
</resultMap>
<sql id="Base_Column_List">
tid,tname,version,is_deleted
</sql>
</mapper>
UserMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.learn.mapper.UserMapper">
<resultMap id="BaseResultMap" type="com.learn.pojo.User">
<id property="uid" column="uid" />
<result property="username" column="username" />
<result property="userPwd" column="user_pwd" />
<result property="nickName" column="nick_name" />
<result property="version" column="version" />
<result property="isDeleted" column="is_deleted" />
</resultMap>
<sql id="Base_Column_List">
uid,username,user_pwd,nick_name,version,is_deleted
</sql>
</mapper>
pojo
Headline.java
package com.learn.pojo;
import com.baomidou.mybatisplus.annotation.*;
import java.util.Date;
import lombok.Data;
/**
* @TableName news_headline
*/
@TableName(value ="news_headline")
@Data
public class Headline {
// 全局配置id auto自增
@TableId
private Integer hid;
private String title;
private String article;
private Integer type;
private Integer publisher;
private Integer pageViews;
private Date createTime;
private Date updateTime;
@Version
private Integer version;
// 全局配置 指定逻辑删除字段属性名
private Integer isDeleted;
}Type.java
package com.learn.pojo;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
/**
* @TableName news_type
*/
@TableName(value ="news_type")
@Data
public class Type {
@TableId
private Integer tid;
private String tname;
@Version
private Integer version;
private Integer isDeleted;
}User.java
package com.learn.pojo;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
/**
* @TableName news_user
*/
@TableName(value ="news_user")
@Data
public class User {
@TableId
private Integer uid;
private String username;
private String userPwd;
private String nickName;
@Version
private Integer version;
private Integer isDeleted;
}pojo/vo/PortalVo.java
package com.learn.pojo.vo;
import lombok.Data;
// 定义vo(Value Object)接收前端传过来的参数
// vo 常用于接收前端表单提交、JSON请求体等
// vo 与DTO或Entity区分开,VO通常只包含当前接口需要的字段,避免直接暴露数据库实体
@Data
public class PortalVo {
private String keyWords;
private Integer type = 0;
private Integer pageNum = 1;
private Integer pageSize = 10;
}
mapper
HeadlineMapper.java
package com.learn.mapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.learn.pojo.Headline;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.learn.pojo.vo.PortalVo;
import org.apache.ibatis.annotations.Param;
import java.util.Map;
/**
* @author DELL
* @description 针对表【news_headline】的数据库操作Mapper
* @createDate 2025-10-25 18:06:42
* @Entity com.learn.pojo.Headline
*/
public interface HeadlineMapper extends BaseMapper<Headline> {
// 自定义查询头条新闻
IPage<Map> selectMyPage(IPage page, @Param("portalVo") PortalVo portalVo);
Map queryDetailMap(Integer hid);
}
TypeMapper.java
package com.learn.mapper;
import com.learn.pojo.Type;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* @author DELL
* @description 针对表【news_type】的数据库操作Mapper
* @createDate 2025-10-25 18:06:42
* @Entity com.learn.pojo.Type
*/
public interface TypeMapper extends BaseMapper<Type> {
}UserMapper.java
package com.learn.mapper;
import com.learn.pojo.User;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* @author DELL
* @description 针对表【news_user】的数据库操作Mapper
* @createDate 2025-10-25 18:06:42
* @Entity com.learn.pojo.User
*/
public interface UserMapper extends BaseMapper<User> {
}service
HeadlineService.java
package com.learn.service;
import com.learn.pojo.Headline;
import com.baomidou.mybatisplus.extension.service.IService;
import com.learn.pojo.vo.PortalVo;
import com.learn.utils.Result;
/**
* @author DELL
* @description 针对表【news_headline】的数据库操作Service
* @createDate 2025-10-25 18:06:42
*/
public interface HeadlineService extends IService<Headline> {
/**
* 首页数据查询
* @param portalVo
* @return
*/
Result findNewsPage(PortalVo portalVo);
/**
* 根据id查询详情
* @param hid
* @return
*/
Result showHeadlineDetail(Integer hid);
/**
* 发布头条
* @param headline
* @return
*/
Result publish(Headline headline, String token);
/**
* 修改头条数据
* @param headline
* @return
*/
Result updateData(Headline headline);
}
TypeService.java
package com.learn.service;
import com.learn.pojo.Type;
import com.baomidou.mybatisplus.extension.service.IService;
import com.learn.utils.Result;
/**
* @author DELL
* @description 针对表【news_type】的数据库操作Service
* @createDate 2025-10-25 18:06:42
*/
public interface TypeService extends IService<Type> {
/**
* 获取所有类型
* @return
*/
Result findAllTypes();
}
UserService.java
package com.learn.service;
import com.learn.pojo.User;
import com.baomidou.mybatisplus.extension.service.IService;
import com.learn.utils.Result;
/**
* @author DELL
* @description 针对表【news_user】的数据库操作Service
* @createDate 2025-10-25 18:06:42
*/
public interface UserService extends IService<User> {
/**
* 登录业务
* @param user
* @return
*/
Result login(User user);
/**
* 根据token获取用户数据
* @param token
* @return
*/
Result getUserInfo(String token);
/**
* 检查用户名是否可用
* @param username
* @return
*/
Result checkUserName(String username);
/**
* 用户注册
* @param user
* @return
*/
Result regist(User user);
}
service/impl
HeadlineServiceImpl.java
package com.learn.service.impl;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.learn.pojo.Headline;
import com.learn.pojo.vo.PortalVo;
import com.learn.service.HeadlineService;
import com.learn.mapper.HeadlineMapper;
import com.learn.utils.JwtHelper;
import com.learn.utils.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author DELL
* @description 针对表【news_headline】的数据库操作Service实现
* @createDate 2025-10-25 18:06:42
*/
@Service
public class HeadlineServiceImpl extends ServiceImpl<HeadlineMapper, Headline>
implements HeadlineService {
@Autowired
private HeadlineMapper headlineMapper;
@Autowired
private JwtHelper jwtHelper;
/**
* 首页数据查询
* 1. 进行分页数据查询
* 2. 分页数据拼接到Result
* <p>
* 注意1:插叙需要自定义语句 自定义mapper方法 携带分页
* 注意2:返回的结果List<Map>
*
* @param portalVo
* @return
*/
@Override
public Result findNewsPage(PortalVo portalVo) {
Page<Map> page = new Page<>(portalVo.getPageNum(), portalVo.getPageSize());
headlineMapper.selectMyPage(page, portalVo);
List<Map> records = page.getRecords();
Map data = new HashMap();
data.put("pageData", records);
Map pageInfo = new HashMap();
pageInfo.put("pageData", data);
pageInfo.put("pageNum", page.getCurrent());
pageInfo.put("pageSize", page.getSize());
pageInfo.put("totalPage", page.getPages());
pageInfo.put("totalSize", page.getTotal());
return Result.ok(pageInfo);
}
/**
* 根据id查询详情
* 1. 查询对应的数据:多表查询 自定义方法 没有对应实体类返回Map
* 2. 修改阅读量 + 1 version乐观锁 当前数据对应的版本
*
* @param hid
* @return
*/
@Override
public Result showHeadlineDetail(Integer hid) {
Map data = headlineMapper.queryDetailMap(hid);
Map headlineMap = new HashMap();
headlineMap.put("headline", data);
// 修改阅读量 + 1
Headline headline = new Headline();
headline.setHid((Integer) data.get("hid"));
headline.setVersion((Integer) data.get("version"));
headline.setPageViews((Integer) data.get("pageViews") + 1);
headlineMapper.updateById(headline);
return Result.ok(headlineMap);
}
/**
* 发布头条
* 1. 补全数据
*
* @param headline
* @return
*/
@Override
public Result publish(Headline headline, String token) {
// 根据token获取用户id
int userId = jwtHelper.getUserId(token).intValue();
// 数据装配
headline.setPublisher(userId);
headline.setPageViews(0);
headline.setCreateTime(new Date());
headline.setUpdateTime(new Date());
headlineMapper.insert(headline);
return Result.ok(null);
}
/**
* 修改头条数据
* 1. hid查询数据的最新version
* 2. 修改数据的修改时间为当前时间
*
* @param headline
* @return
*/
@Override
public Result updateData(Headline headline) {
Integer version = headlineMapper.selectById(headline.getHid()).getVersion();
headline.setVersion(version); // 乐观锁
headline.setUpdateTime(new Date());
headlineMapper.updateById(headline);
return Result.ok(null);
}
}
TypeServiceImpl.java
package com.learn.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.learn.pojo.Type;
import com.learn.service.TypeService;
import com.learn.mapper.TypeMapper;
import com.learn.utils.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* @author DELL
* @description 针对表【news_type】的数据库操作Service实现
* @createDate 2025-10-25 18:06:42
*/
@Service
public class TypeServiceImpl extends ServiceImpl<TypeMapper, Type>
implements TypeService{
@Autowired
private TypeMapper typeMapper;
/**
* 查找所有分类
* @return
*/
@Override
public Result findAllTypes() {
List<Type> types = typeMapper.selectList(null);
return Result.ok(types);
}
}UserServiceImpl.java
package com.learn.service.impl;
import com.alibaba.druid.util.StringUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.learn.pojo.User;
import com.learn.service.UserService;
import com.learn.mapper.UserMapper;
import com.learn.utils.JwtHelper;
import com.learn.utils.MD5Util;
import com.learn.utils.Result;
import com.learn.utils.ResultCodeEnum;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
/**
* @author DELL
* @description 针对表【news_user】的数据库操作Service实现
* @createDate 2025-10-25 18:06:42
*/
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
implements UserService {
@Autowired
private UserMapper userMapper;
@Autowired
private JwtHelper jwtHelper;
/**
* 登录业务
* 1. 根据账号查询用户对象
* 2. 如果用户对象为null,查询失败,账号错误 501
* 3. 对比密码,失败 返回503
* 4. 更具用户id生成token token -> result 返回
*
* @param user
* @return
*/
@Override
public Result login(User user) {
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(User::getUsername, user.getUsername());
User loginUser = userMapper.selectOne(lambdaQueryWrapper);
if (loginUser == null) {
return Result.build(null, ResultCodeEnum.USERNAME_ERROR);
}
// 对比密码
if (!StringUtils.isEmpty(user.getUserPwd())
&& MD5Util.encrypt(user.getUserPwd()).equals(loginUser.getUserPwd())) {
// 登录成功
// 根据有用户id生成token
// 将token封装到result返回
String token = jwtHelper.createToken(loginUser.getUid().longValue());
Map data = new HashMap();
data.put("token", token);
return Result.ok(data);
}
// 密码错误
return Result.build(null, ResultCodeEnum.PASSWORD_ERROR);
}
/**
* 根据token获取用户数据
* 1. 校验token有效性 是否在有效期
* 2. 根据token解析用户id
* 3. 根据用户id获取用户数据
* 4. 去掉密码 封装result结果返回
*/
@Override
public Result getUserInfo(String token) {
// token是否过期
boolean expiration = jwtHelper.isExpiration(token);
if (expiration) {
return Result.build(null, ResultCodeEnum.NOTLOGIN);
}
int userId = jwtHelper.getUserId(token).intValue();
User user = userMapper.selectById(userId);
user.setUserPwd("");
Map data = new HashMap();
data.put("loginUser", user);
return Result.ok(data);
}
/**
* 检查用户名是否可用
* 1. 检查账号 进行count查询
* 2. count = 0 可用
* 3. count > 0 不可用
*/
@Override
public Result checkUserName(String username) {
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(User::getUsername, username);
Long count = userMapper.selectCount(lambdaQueryWrapper);
if (count == 0) {
return Result.ok(null);
}
return Result.build(null, ResultCodeEnum.USERNAME_USED);
}
/**
* 用户注册
* 1. 检查用户是否注册
* 2. 密码加密
* 3. 账号数据保存
* 4. 返回结果
* @param user
* @return
*/
@Override
public Result regist(User user) {
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(User::getUsername, user.getUsername());
Long count = userMapper.selectCount(lambdaQueryWrapper);
if (count > 0) {
return Result.build(null, ResultCodeEnum.USERNAME_USED);
}
user.setUserPwd(MD5Util.encrypt(user.getUserPwd()));
userMapper.insert(user);
return Result.ok(null);
}
}controller
HeadlineController.java
package com.learn.controller;
import com.learn.pojo.Headline;
import com.learn.service.HeadlineService;
import com.learn.utils.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/headline")
@CrossOrigin
public class HeadlineController {
@Autowired
private HeadlineService headlineService;
// 登录后才可以访问
@PostMapping("/publish")
public Result publish(@RequestBody Headline headline, @RequestHeader String token) {
Result result = headlineService.publish(headline, token);
return result;
}
@PostMapping("/findHeadlineById")
public Result findHeadlineById(Integer hid) {
Headline headline = headlineService.getById(hid);
Map data = new HashMap();
data.put("headline", headline);
return Result.ok(data);
}
@PostMapping("/update")
public Result update(@RequestBody Headline headline) {
Result result = headlineService.updateData(headline);
return result;
}
@PostMapping("/removeByHid")
public Result removeByHid(Integer hid) {
headlineService.removeById(hid);
return Result.ok(null);
}
}
PortalController.java
package com.learn.controller;
import com.learn.pojo.vo.PortalVo;
import com.learn.service.HeadlineService;
import com.learn.service.TypeService;
import com.learn.utils.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/portal")
@CrossOrigin
public class PortalController {
@Autowired
private TypeService typeService;
@Autowired
private HeadlineService headlineService;
@GetMapping("/findAllTypes")
public Result findAllTypes() {
Result result = typeService.findAllTypes();
return result;
}
@PostMapping("/findNewsPage")
public Result findNewsPage(@RequestBody PortalVo portalVo) {
Result result = headlineService.findNewsPage(portalVo);
return result;
}
@PostMapping("/showHeadlineDetail")
public Result showHeadlineDetail(Integer hid) {
Result result = headlineService.showHeadlineDetail(hid);
return result;
}
}
UserController.java
package com.learn.controller;
import com.learn.pojo.User;
import com.learn.service.UserService;
import com.learn.utils.JwtHelper;
import com.learn.utils.Result;
import com.learn.utils.ResultCodeEnum;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/user")
@CrossOrigin // 跨域
public class UserController {
@Autowired
private JwtHelper jwtHelper;
@Autowired
private UserService userService;
@PostMapping("/login")
public Result login(@RequestBody User user) {
Result result = userService.login(user);
return result;
}
@GetMapping("/getUserInfo")
public Result getUserInfo(@RequestHeader String token) {
Result result = userService.getUserInfo(token);
return result;
}
@PostMapping("/checkUserName")
public Result checkUserName(String username) {
Result result = userService.checkUserName(username);
return result;
}
@PostMapping("/regist")
public Result regist(@RequestBody User user) {
Result result = userService.regist(user);
return result;
}
@GetMapping("/checkLogin")
public Result checkLogin(@RequestHeader String token) {
boolean expiration = jwtHelper.isExpiration(token);
if (expiration) {
return Result.build(null, ResultCodeEnum.NOTLOGIN);
}
return Result.ok(null);
}
}
interceptors
LoginProtectedInterceptor.java
package com.learn.interceptors;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.learn.utils.JwtHelper;
import com.learn.utils.Result;
import com.learn.utils.ResultCodeEnum;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
/**
* 登录保护拦截器,检查请求头是否包含有效token
* 有效,放行
* 无效,504
*/
@Component
public class LoginProtectedInterceptor implements HandlerInterceptor {
@Autowired
private JwtHelper jwtHelper;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 从请求头中获取token
String token = request.getHeader("token");
// 检查是否有效
boolean expiration = jwtHelper.isExpiration(token);
// 有效放行
if (!expiration) {
return true;
}
// 无效返回504
Result result = Result.build(null, ResultCodeEnum.NOTLOGIN);
ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(result);
response.getWriter().print(json);
return false;
}
}
config
WebMVCConfig.java
package com.learn.config;
import com.learn.interceptors.LoginProtectedInterceptor;
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 WebMVCConfig implements WebMvcConfigurer {
@Autowired
private LoginProtectedInterceptor loginProtectedInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginProtectedInterceptor).addPathPatterns("/headline/**");
}
}
utils
JwtHelper.java
package com.learn.utils;
import com.alibaba.druid.util.StringUtils;
import io.jsonwebtoken.*;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.Date;
@Data
@Component // 被此注解修饰的类会被自动检测并注册为Spring bean
@ConfigurationProperties(prefix = "jwt.token")
public class JwtHelper {
private long tokenExpiration; // 有效时间单位毫秒
private String tokenSignKey; // 当前程序签名密钥
// 生成token字符串
public String createToken(Long userId) {
System.out.println("tokenExpiration = " + tokenExpiration);
System.out.println("tokenSignKey = " + tokenSignKey);
String token = Jwts.builder()
.setSubject("YYGH-USER")
.setExpiration(new Date(System.currentTimeMillis() + tokenExpiration * 1000 * 60))
.claim("userId", userId)
.signWith(SignatureAlgorithm.HS512, tokenSignKey)
.compressWith(CompressionCodecs.GZIP)
.compact();
return token;
}
// 从token字符串获取userId
public Long getUserId(String token) {
if (StringUtils.isEmpty(token)) {
return null;
}
Jws<Claims> claimsJws = Jwts.parser().setSigningKey(tokenSignKey).parseClaimsJws(token);
Claims claims = claimsJws.getBody();
Integer userId = (Integer) claims.get("userId");
return userId.longValue();
}
// 判断token是否有效
public boolean isExpiration(String token) {
try {
boolean isExpire = Jwts.parser()
.setSigningKey(tokenSignKey)
.parseClaimsJws(token)
.getBody()
.getExpiration()
.before(new Date());
// 没有过期,有效返回false
return isExpire;
} catch (Exception e) {
return true;
}
}
}
MD5Util.java
package com.learn.utils;
import org.springframework.stereotype.Component;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
* MD5 加密工具类
* 用于对用户密码等敏感信息进行不可逆加密
*/
@Component
public class MD5Util {
/**
* 对输入字符串进行 MD5 加密
*
* @param strSrc 待加密的原始字符串(如用户密码)
* @return 加密后的 32 位小写十六进制字符串
*/
public static String encrypt(String strSrc) {
if (strSrc == null || strSrc.isEmpty()) {
throw new IllegalArgumentException("输入不能为空");
}
try {
// 获取 MD5 消息摘要实例
MessageDigest md = MessageDigest.getInstance("MD5");
// 将输入字符串转换为字节数组并进行摘要计算
byte[] digest = md.digest(strSrc.getBytes());
// 将字节数组转换为十六进制字符串
StringBuilder hexString = new StringBuilder();
for (byte b : digest) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
return hexString.toString(); // 返回 32 位小写 MD5 字符串
} catch (NoSuchAlgorithmException e) {
// 理论上不会发生,因为 MD5 是标准算法
throw new RuntimeException("MD5加密出错!!", e);
}
}
public static void main(String[] args) {
String encrypt = encrypt("12345");
System.out.println(encrypt);
}
}Result.java
package com.learn.utils;
/**
* 全局统一返回结果类
*/
public class Result<T> {
// 返回码
private Integer code;
// 返回消息
private String message;
// 返回数据
private T data;
public Result() {
}
// 返回数据
protected static <T> Result<T> build(T data) {
Result<T> result = new Result<>();
if (data != null) {
result.setData(data);
}
return result;
}
public static <T> Result<T> build(T body, Integer code, String message) {
Result<T> result = build(body);
result.setCode(code);
result.setMessage(message);
return result;
}
public static <T> Result<T> build(T body, ResultCodeEnum resultCodeEnum) {
Result<T> result = build(body);
result.setCode(resultCodeEnum.getCode());
result.setMessage(resultCodeEnum.getMessage());
return result;
}
public static <T> Result<T> ok(T data) {
Result<T> result = build(data);
return build(data, ResultCodeEnum.SUCCESS);
}
public Result<T> message(String msg) {
this.setMessage(msg);
return this;
}
public Result<T> code(Integer code) {
this.setCode(code);
return this;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
ResultCodeEnum.java
package com.learn.utils;
public enum ResultCodeEnum {
SUCCESS(200, "success"),
USERNAME_ERROR(501, "usernameError"),
PASSWORD_ERROR(503, "passwordError"),
NOTLOGIN(504, "notLogin"),
USERNAME_USED(505, "usernameUsed");
private Integer code;
private String message;
private ResultCodeEnum(Integer code, String message) {
this.code = code;
this.message = message;
}
public Integer getCode() {
return code;
}
public String getMessage() {
return message;
}
}
Main.java
package com.learn;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
@MapperScan("com.learn.mapper")
@SpringBootApplication
public class Main {
public static void main(String[] args) {
SpringApplication.run(Main.class, args);
}
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 分页
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
// 乐观锁插件
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
// 防止全表删除、更新
interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
return interceptor;
}
}