<?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 https://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.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.10.3</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.73</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<!-- 阿里镜像 -->
<repositories>
<repository>
<id>aliyun</id>
<url>https://maven.aliyun.com/repository/public</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>aliyun-plugin</id>
<url>https://maven.aliyun.com/repository/public</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
</project>
create database store; # 新建数据库
use store; # 选中数据库
create table t_user( # 新建数据表
uid varchar(50) not null primary key,
username varchar(20) not null unique,
password char(50) not null,
salt varchar(50),
phone varchar(11) unique,
email varchar(30) unique not null,
gender int(1),
avatar varchar(50),
is_delete int(1) not null,
created_user varchar(20) not null,
created_time datetime not null,
modified_user varchar(20) not null,
modified_time datetime not null
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver # 驱动
username: root #数据库登录名
password: 123456 #数据库密码
# 数据库名为store,字符集为utf-8,时区为北京
url: jdbc:mysql://localhost:3306/store?characterEncoding=utf-8&serverTimezone=GMT%2B8
mybatis:
mapper-locations: classpath:mapper/*.xml # mapper.xml扫描路径
package com.example.demo;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication // 注册为springboot启动类
@MapperScan("com.example.demo.mapper") // 扫描mapper路径也就是dao层
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
6.在demo目录下新建mapper目录,在demo/mapper下新建UserMapper类
package com.example.demo.mapper;
import com.example.demo.entity.User;
import org.apache.ibatis.annotations.Param;
import java.util.Date;
public interface UserMapper {
/**
* 插入用户的数据
*
* @param user 用户的数据
* @return 返回Integer
*/
Integer insert(User user);
/**
* 根据用户名来查询用户的数据
*
* @param username 用户名
* @return 返回User或者null
*/
User findByUsername(String username);
Integer updatePasswordByUid(@Param("uid") String uid,
@Param("password") String password,
@Param("modifiedUser") String modifiedUser,
@Param("modifiedTime") Date modifiedTime);
/*** 根据用户id查询用户数据
* @param uid 用户id
* @return 匹配的用户数据,如果没有匹配的用户数据,则返回null
*/
User findByUid(String uid);
}
package com.example.demo.Service.ex;
/**
* 重写异常方法
*/
public class ServiceException extends RuntimeException {
public ServiceException() {
super();
}
public ServiceException(String message) {
super(message);
}
public ServiceException(String message, Throwable cause) {
super(message, cause);
}
public ServiceException(Throwable cause) {
super(cause);
}
protected ServiceException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
package com.example.demo.Service.ex;
public class InsertException extends ServiceException {
public InsertException() {
super();
}
public InsertException(String message) {
super(message);
}
public InsertException(String message, Throwable cause) {
super(message, cause);
}
public InsertException(Throwable cause) {
super(cause);
}
protected InsertException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
package com.example.demo.Service;
import com.example.demo.entity.User;
public interface IUserService {
/**
* 用户注册方法
* @param user
*/
void reg(User user);
/**
* 用户登录
* @param username
* @param password
* @return
*/
User login(String username,String password);
/**
* 修改密码
* @param uid 当前登录的id
* @param username 用户名
* @param oldPassword
* @param newPassword
*/
void changePassword(String uid, String username, String oldPassword, String newPassword);
/**
* 根据用户id查询用户信息,有token时才可访问
* @param uid 当前登录的id
* @param username 用户名
* @param oldPassword
* @param newPassword
*/
User findByUid(String uid);
}
package com.example.demo.Service.impl;
import com.example.demo.Service.IUserService;
import com.example.demo.Service.ex.PasswordNotMatchException;
import com.example.demo.Service.ex.UpdateException;
import com.example.demo.Service.ex.UserNotFoundException;
import com.example.demo.Service.ex.UsernameDuplicatedException;
import com.example.demo.entity.User;
import com.example.demo.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;
import java.util.Date;
import java.util.UUID;
@Service
public class UserServiceImpl implements IUserService {
@Autowired
private UserMapper userMapper;
@Override
public void reg(User user) { // 用户注册
// 查重
User result = userMapper.findByUsername(user.getUsername());
if (result != null) {
throw new UsernameDuplicatedException("用户名被占用");
}
// 设置id
user.setUid(UUID.randomUUID().toString().toUpperCase());
// 设置初始值
user.setIsDelete(0);
user.setCreatedUser(user.getUsername());
user.setModifiedUser(user.getUsername());
// 时间
Date date = new Date();
user.setModifiedTime(date);
user.setCreatedTime(date);
// 密码加密
String oldPassword = user.getPassword();
String salt = UUID.randomUUID().toString().toUpperCase();
String md5Password = getMd5Pw(oldPassword, salt);
// 保存密码
user.setPassword(md5Password);
// 保存盐值
user.setSalt(salt);
// 注册
Integer rows = userMapper.insert(user);
if (rows != 1) {
throw new UsernameDuplicatedException("在用户注册过程中发生了未知的异常");
}
}
@Override
public User login(String username, String password) { // 登录
User result = userMapper.findByUsername(username);
if (result == null) {
throw new UserNotFoundException("用户数据不存在的异常");
}
if (result.getIsDelete() == 1) {
throw new UserNotFoundException("用户数据不存在的错误");
}
// 获取盐值
String salt = result.getSalt();
String md5Pw = getMd5Pw(password, salt);
if (!result.getPassword().equals(md5Pw)) {
throw new PasswordNotMatchException("密码验证失败的错误");
}
result.setPassword("");
return result;
}
@Override
public void changePassword(String uid, String username, String oldPassword, String newPassword) { // 修改密码
User result = userMapper.findByUid(uid);
if (result == null) {
throw new UserNotFoundException("用户数据不存在");
}
if (result.getIsDelete().equals(1)) {
throw new UserNotFoundException("用户数据不存在");
}
String salt = result.getSalt();
// 比较密码
String oldMd5Pw = getMd5Pw(oldPassword, salt);
if (!result.getPassword().contentEquals(oldMd5Pw)) {
throw new PasswordNotMatchException("原密码错误");
}
// 新密码
String newMd5Pw = getMd5Pw(newPassword, salt);
Date now = new Date();
Integer rows = userMapper.updatePasswordByUid(uid, newMd5Pw, username, now);
if (rows != 1) {
throw new UpdateException("更新用户数据时出现未知错误,请联系统管理员");
}
}
@Override
public User findByUid(String uid) { // 根据uid查询用户资料
return userMapper.findByUid(uid);
}
/**
* 密码通过md5算法加密
*/
private String getMd5Pw(String password, String salt) {
for (int i = 0; i < 3; i++) {
password = DigestUtils.md5DigestAsHex((salt + password).getBytes()).toUpperCase();
}
return password;
}
}
package com.example.demo.controll;
import com.example.demo.Service.ex.*;
import com.example.demo.util.JsonResult;
import org.springframework.web.bind.annotation.ExceptionHandler;
/**
* 控制层基类
*/
public class BaseController {
// 成功状态码
public static final int OK = 200;
// 请求处理方法,这个方法的返回值就是需要传递给前端的数据
@ExceptionHandler(ServiceException.class) // 统一处理抛出的异常
public JsonResult<Void> handlerException(Throwable e) {
JsonResult<Void> result = new JsonResult<>(e);
if(e instanceof UsernameDuplicatedException){
result.setState(400);
result.setMessage("用户名已经被占用");
}else if(e instanceof UserNotFoundException){
result.setState(500);
result.setMessage("用户不存在");
}else if(e instanceof PasswordNotMatchException){
result.setState(600);
result.setMessage("密码错误");
}else if(e instanceof InsertException){
result.setMessage("注册失败");
result.setState(700);
}else if(e instanceof UpdateException){
result.setState(800);
result.setMessage("修改密码失败");
}
return result;
}
}
package com.example.demo.controll;
import com.example.demo.Service.IUserService;
import com.example.demo.entity.User;
import com.example.demo.util.JWTUtils;
import com.example.demo.util.JsonResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("users")
public class UserController extends BaseController {
@Autowired
private IUserService userService;
// 用户注册
@RequestMapping("/reg")
public JsonResult<Void> reg(User user) {
userService.reg(user);
return new JsonResult<>(OK, "用户注册成功");
}
// 用户登录
@RequestMapping("/login")
public JsonResult<User> login(String username, String password) {
User user = userService.login(username, password);
if (user != null) {
Map<String,String> map = new HashMap<>();
map.put("username",user.getUsername());
map.put("uid",user.getUid());
String token = JWTUtils.getToken(map);
user.setToken(token);
}
return new JsonResult<>(OK, "登录成功", user);
}
// 修改密码
@RequestMapping("/change_password")
public JsonResult<Void> changePassword(String uid, String username, String oldPassword, String newPassword) {
userService.changePassword(uid, username, oldPassword, newPassword);
return new JsonResult<>(OK, "修改成功");
}
// 获取用户信息
@RequestMapping("/list")
public JsonResult<User> list(String uid) {
User user = userService.findByUid(uid);
return new JsonResult<>(OK,"获取成功",user);
}
}
package com.example.demo.util;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import java.util.Calendar;
import java.util.Map;
public class JWTUtils {
// 秘钥
private static final String SING = "!Q@W3e4r%T^Y";
/**
* 生成token header.payload.sing
*/
public static String getToken(Map<String,String> map){
Calendar instance = Calendar.getInstance();
instance.add(Calendar.DATE,7);//默认7天过期
//创建jwt builder
JWTCreator.Builder builder = JWT.create();
// 将传进来的用户信息加入到token再生成
map.forEach((k,v)->{
builder.withClaim(k,v);
});
// 得到token
String token = builder.withExpiresAt(instance.getTime())//指定令牌过期时间
.sign(Algorithm.HMAC256(SING));// 机密方式为HMAC256,SING秘钥
return token;
}
/**
* 验证token 合法性
*
*/
public static DecodedJWT verify(String token){
return JWT.require(Algorithm.HMAC256(SING)).build().verify(token);
}
}
package com.example.demo.util;
import java.io.Serializable;
public class JsonResult<E> implements Serializable {
// 状态码
private Integer state;
// 描述信息
private String message;
// 数据
private E data;
public JsonResult() {
}
public JsonResult(Integer state) {
this.state = state;
}
public JsonResult(Throwable e) {
this.message = e.getMessage();
}
public JsonResult(Integer state, E data) {
this.state = state;
this.data = data;
}
public Integer getState() {
return state;
}
public void setState(Integer state) {
this.state = state;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public E getData() {
return data;
}
public void setData(E data) {
this.data = data;
}
public JsonResult(Integer state, String message) {
this.state = state;
this.message = message;
}
public JsonResult(Integer state, String message, E data) {
this.state = state;
this.message = message;
this.data = data;
}
}
package com.example.demo.entity;
import java.io.Serializable;
import java.util.Date;
import java.util.Objects;
public class BaseEntity implements Serializable {
private String createdUser;
private Date createdTime;
private String modifiedUser;
private Date modifiedTime;
public String getCreatedUser() {
return createdUser;
}
public void setCreatedUser(String createdUser) {
this.createdUser = createdUser;
}
public Date getCreatedTime() {
return createdTime;
}
public void setCreatedTime(Date createdTime) {
this.createdTime = createdTime;
}
public String getModifiedUser() {
return modifiedUser;
}
public void setModifiedUser(String modifiedUser) {
this.modifiedUser = modifiedUser;
}
public Date getModifiedTime() {
return modifiedTime;
}
public void setModifiedTime(Date modifiedTime) {
this.modifiedTime = modifiedTime;
}
@Override
public String toString() {
return "BaseEntity{" +
"createdUser='" + createdUser + '\'' +
", createdTime=" + createdTime +
", modifiedUser='" + modifiedUser + '\'' +
", modifiedTime=" + modifiedTime +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof BaseEntity)) return false;
BaseEntity that = (BaseEntity) o;
return Objects.equals(getCreatedUser(), that.getCreatedUser()) &&
Objects.equals(getCreatedTime(), that.getCreatedTime()) &&
Objects.equals(getModifiedUser(), that.getModifiedUser()) &&
Objects.equals(getModifiedTime(), that.getModifiedTime());
}
@Override
public int hashCode() {
return Objects.hash(getCreatedUser(), getCreatedTime(), getModifiedUser(), getModifiedTime());
}
}
package com.example.demo.entity;
import java.util.Objects;
public class User extends BaseEntity {
private String uid;
private String username;
private String password;
private String salt;
private String phone;
private String email;
private Integer gender;
private String avatar;
private Integer isDelete;
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
private String token;
public String getUid() {
return uid;
}
public void setUid(String uid) {
this.uid = uid;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getSalt() {
return salt;
}
public void setSalt(String salt) {
this.salt = salt;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Integer getGender() {
return gender;
}
public void setGender(Integer gender) {
this.gender = gender;
}
public String getAvatar() {
return avatar;
}
public void setAvatar(String avatar) {
this.avatar = avatar;
}
public Integer getIsDelete() {
return isDelete;
}
public void setIsDelete(Integer isDelete) {
this.isDelete = isDelete;
}
@Override
public String toString() {
return "User{" +
"uid='" + uid + '\'' +
", username='" + username + '\'' +
", password='" + password + '\'' +
", salt='" + salt + '\'' +
", phone='" + phone + '\'' +
", email='" + email + '\'' +
", gender=" + gender +
", avatar='" + avatar + '\'' +
", isDelete=" + isDelete +
", token='" + token + '\'' +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof User)) return false;
if (!super.equals(o)) return false;
User user = (User) o;
return Objects.equals(getUid(), user.getUid()) &&
Objects.equals(getUsername(), user.getUsername()) &&
Objects.equals(getPassword(), user.getPassword()) &&
Objects.equals(getSalt(), user.getSalt()) &&
Objects.equals(getPhone(), user.getPhone()) &&
Objects.equals(getEmail(), user.getEmail()) &&
Objects.equals(getGender(), user.getGender()) &&
Objects.equals(getAvatar(), user.getAvatar()) &&
Objects.equals(getIsDelete(), user.getIsDelete()) &&
Objects.equals(getToken(), user.getToken());
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), getUid(), getUsername(), getPassword(), getSalt(), getPhone(), getEmail(), getGender(), getAvatar(), getIsDelete(), getToken());
}
}
package com.example.demo.interceptor;
import com.auth0.jwt.exceptions.AlgorithmMismatchException;
import com.auth0.jwt.exceptions.SignatureVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.example.demo.util.JWTUtils;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
public class JWTInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Map<String, Object> map = new HashMap<>();
//获取请求头中令牌
String token = request.getHeader("token");
try {
JWTUtils.verify(token);//验证令牌
return true;//放行请求
} catch (SignatureVerificationException e) {
e.printStackTrace();
map.put("msg","无效签名!");
}catch (TokenExpiredException e){
e.printStackTrace();
map.put("msg","token过期!");
}catch (AlgorithmMismatchException e){
e.printStackTrace();
map.put("msg","token算法不一致!");
}catch (Exception e){
e.printStackTrace();
map.put("msg","token无效!!");
}
map.put("state",false);//设置状态
//将map 专为json jackson
String json = new ObjectMapper().writeValueAsString(map);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().println(json);
return false;
}
}
package com.example.demo.config;
import com.example.demo.interceptor.JWTInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ConcurrentTaskExecutor;
import org.springframework.web.servlet.config.annotation.AsyncSupportConfigurer;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new JWTInterceptor())
.addPathPatterns("/users/list") //其他接口token验证
.excludePathPatterns("/users/login"); //所有用户都放行
}
// 最大30线程,外加30列队;【拒绝策略AbortPolicy(默认):丢弃并抛出异常】
private static ExecutorService executorService = new ThreadPoolExecutor(
1, 30, 60L,
TimeUnit.SECONDS, new ArrayBlockingQueue<>(3)
);
/**
* 线程限制
*
* @param configurer
*/
@Override
public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
configurer.setTaskExecutor(new ConcurrentTaskExecutor(executorService));
configurer.setDefaultTimeout(30000);
}
}