首先源码https://gitee.com/mobile191llx/spring-boot-jwt.git
接着项目目录结构
项目开始
1.新建项目名称为demo、选择maven什么都不要勾选
2.依赖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 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>
3.新建数据库
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;
4.配置项目application.yml
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
5.主启动类、在java路径下创建com.example.demo目录、在demo目录下创建App类
package com.example.demo;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.example.demo.mapper")
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 {
Integer insert(User user);
User findByUsername(String username);
Integer updatePasswordByUid(@Param("uid") String uid,
@Param("password") String password,
@Param("modifiedUser") String modifiedUser,
@Param("modifiedTime") Date modifiedTime);
User findByUid(String uid);
}
7 .创建demo/service/ex目录、在ex目录下新建ServiceException异常类
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);
}
}
8.创建demo/service/ex目录、在ex目录下新建5个异常类-InsertException、PasswordNotMatchException、UpdateException、UsernameDuplicatedException、UserNotFoundException,这几个类全部继承上面创建的ServiceException类并且insert+alt选择Override Methods选择前4项,大概如下
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);
}
}
9.在service目录下新建IUserService接口
package com.example.demo.Service;
import com.example.demo.entity.User;
public interface IUserService {
void reg(User user);
User login(String username,String password);
void changePassword(String uid, String username, String oldPassword, String newPassword);
User findByUid(String uid);
}
10.在service目录下创建UserServiceImpl
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("用户名被占用");
}
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) {
return userMapper.findByUid(uid);
}
private String getMd5Pw(String password, String salt) {
for (int i = 0; i < 3; i++) {
password = DigestUtils.md5DigestAsHex((salt + password).getBytes()).toUpperCase();
}
return password;
}
}
10.在demo目录下新建controller目录,在controller目录下新建BaseController类用于返回异常结果
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;
}
}
11.在controller目录下新建UserController类
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);
}
}
12.在demo下新建utils目录,在utils下新建JWTUtils类
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";
public static String getToken(Map<String,String> map){
Calendar instance = Calendar.getInstance();
instance.add(Calendar.DATE,7);
JWTCreator.Builder builder = JWT.create();
map.forEach((k,v)->{
builder.withClaim(k,v);
});
String token = builder.withExpiresAt(instance.getTime())
.sign(Algorithm.HMAC256(SING));
return token;
}
public static DecodedJWT verify(String token){
return JWT.require(Algorithm.HMAC256(SING)).build().verify(token);
}
}
13.在utils目录下新建JsonResult类用于封装返回结果集
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;
}
}
14.忘了还有两个实体类、在demo下创建entity目录,在entity下创建BaseEntity类
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());
}
}
15.entity目录下继续新建User类
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());
}
}
16.配置拦截规则、在demo下新建interceptor目录,在interceptor新建JWTinterceptor类
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);
String json = new ObjectMapper().writeValueAsString(map);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().println(json);
return false;
}
}
17.在demo下新建config,在config目录下新建InterceptorConfig
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")
.excludePathPatterns("/users/login");
}
private static ExecutorService executorService = new ThreadPoolExecutor(
1, 30, 60L,
TimeUnit.SECONDS, new ArrayBlockingQueue<>(3)
);
@Override
public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
configurer.setTaskExecutor(new ConcurrentTaskExecutor(executorService));
configurer.setDefaultTimeout(30000);
}
}
18.运行app、获取users/list不带token
19.登录一下、返回token和用户信息
20.在访问users/list
Ok,到这里就结束了
|