lrf 8 kuukautta sitten
vanhempi
commit
c370e466f3
32 muutettua tiedostoa jossa 1267 lisäystä ja 109 poistoa
  1. 39 0
      .gitignore
  2. 126 0
      pom.xml
  3. 4 0
      readme.md
  4. 22 0
      src/main/java/com/free/InterceptorConfig.java
  5. 56 0
      src/main/java/com/free/SecurityConfig.java
  6. 10 0
      src/main/java/com/free/SwaggerConfig.java
  7. 12 0
      src/main/java/com/free/annotation/PassToken.java
  8. 73 0
      src/main/java/com/free/controller/LoginController.java
  9. 14 4
      src/main/java/com/free/controller/TestController.java
  10. 78 0
      src/main/java/com/free/controller/system/AdminController.java
  11. 16 0
      src/main/java/com/free/controller/system/AdminRoleController.java
  12. 14 0
      src/main/java/com/free/dto/LoginDTO.java
  13. 16 0
      src/main/java/com/free/dto/system/admin/AdminCreateDTO.java
  14. 1 1
      src/main/java/com/free/entity/Test.java
  15. 51 0
      src/main/java/com/free/entity/system/Admin.java
  16. 26 0
      src/main/java/com/free/entity/system/AdminRole.java
  17. 62 0
      src/main/java/com/free/entity/system/LoginRecord.java
  18. 58 0
      src/main/java/com/free/frame/AuthenticationInterceptor.java
  19. 35 30
      src/main/java/com/free/frame/ExceptionEnum.java
  20. 50 26
      src/main/java/com/free/frame/GlobalExceptionController.java
  21. 60 37
      src/main/java/com/free/frame/ResponseFormat.java
  22. 11 0
      src/main/java/com/free/mapper/system/AdminMapper.java
  23. 11 0
      src/main/java/com/free/mapper/system/AdminRoleMapper.java
  24. 9 0
      src/main/java/com/free/mapper/system/LoginRecordMapper.java
  25. 12 0
      src/main/java/com/free/service/system/AdminRoleService.java
  26. 12 0
      src/main/java/com/free/service/system/AdminService.java
  27. 12 0
      src/main/java/com/free/service/system/LoginRecordService.java
  28. 111 0
      src/main/java/com/free/utils/BaseController.java
  29. 32 9
      src/main/java/com/free/utils/BaseEntity.java
  30. 24 0
      src/main/java/com/free/utils/BcryptUtil.java
  31. 202 0
      src/main/java/com/free/utils/JwtUtil.java
  32. 8 2
      src/main/resources/application.yml

+ 39 - 0
.gitignore

@@ -0,0 +1,39 @@
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+
+### IntelliJ IDEA ###
+.idea/
+.idea/modules.xml
+.idea/jarRepositories.xml
+.idea/compiler.xml
+.idea/libraries/
+*.iws
+*.iml
+*.ipr
+
+### Eclipse ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/
+
+### Mac OS ###
+.DS_Store

+ 126 - 0
pom.xml

@@ -0,0 +1,126 @@
+<?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>
+  <groupId>com.free</groupId>
+  <artifactId>customer</artifactId>
+  <version>0.0.1-SNAPSHOT</version>
+  <name>customer</name>
+  <description>Demo project for Spring Boot</description>
+  <properties>
+    <java.version>1.8</java.version>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+    <spring-boot.version>2.7.6</spring-boot.version>
+  </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>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-devtools</artifactId>
+      <scope>runtime</scope>
+      <optional>true</optional>
+    </dependency>
+    <!-- 安全,防御,加密 -->
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-security</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>com.auth0</groupId>
+      <artifactId>java-jwt</artifactId>
+      <version>3.8.3</version>
+    </dependency>
+    <!-- 参数校验 -->
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-validation</artifactId>
+    </dependency>
+    <!-- mybatis-plus与数据库    -->
+    <dependency>
+      <groupId>com.baomidou</groupId>
+      <artifactId>mybatis-plus-boot-starter</artifactId>
+      <version>3.5.7</version>
+    </dependency>
+    <dependency>
+      <groupId>com.gitee.sunchenbin.mybatis.actable</groupId>
+      <artifactId>mybatis-enhance-actable</artifactId>
+      <version>1.5.0.RELEASE</version>
+      <exclusions>
+        <exclusion>
+          <groupId>com.baomidou</groupId>
+          <artifactId>mybatis-plus-annotation</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
+    <dependency>
+      <groupId>mysql</groupId>
+      <artifactId>mysql-connector-java</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.projectlombok</groupId>
+      <artifactId>lombok</artifactId>
+    </dependency>
+    <!-- swagger -->
+    <dependency>
+      <groupId>io.springfox</groupId>
+      <artifactId>springfox-boot-starter</artifactId>
+      <version>3.0.0</version>
+    </dependency>
+  </dependencies>
+  <dependencyManagement>
+    <dependencies>
+      <dependency>
+        <groupId>org.springframework.boot</groupId>
+        <artifactId>spring-boot-dependencies</artifactId>
+        <version>${spring-boot.version}</version>
+        <type>pom</type>
+        <scope>import</scope>
+      </dependency>
+    </dependencies>
+  </dependencyManagement>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-compiler-plugin</artifactId>
+        <version>3.8.1</version>
+        <configuration>
+          <source>1.8</source>
+          <target>1.8</target>
+          <encoding>UTF-8</encoding>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.springframework.boot</groupId>
+        <artifactId>spring-boot-maven-plugin</artifactId>
+        <version>${spring-boot.version}</version>
+        <configuration>
+          <mainClass>com.free.customer.CustomerApplication</mainClass>
+          <skip>true</skip>
+          <fork>true</fork>
+          <addResources>true</addResources>
+        </configuration>
+        <executions>
+          <execution>
+            <id>repackage</id>
+            <goals>
+              <goal>repackage</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+
+</project>

+ 4 - 0
readme.md

@@ -0,0 +1,4 @@
+# 说明
+
+## token及验证:
+  自定义拦截器,针对所有路由进行检查. 如果函数使用@PassToken注解,则会跳过检查,否则会检查是否有登录信息.

+ 22 - 0
src/main/java/com/free/InterceptorConfig.java

@@ -0,0 +1,22 @@
+package com.free;
+
+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.WebMvcConfigurer;
+
+import com.free.frame.AuthenticationInterceptor;
+
+@Configuration
+public class InterceptorConfig implements WebMvcConfigurer {
+  @Override
+  public void addInterceptors(InterceptorRegistry registry) {
+    registry.addInterceptor(authenticationInterceptor()).excludePathPatterns("/login/**")
+        .addPathPatterns("/**");
+  }
+
+  @Bean
+  public AuthenticationInterceptor authenticationInterceptor() {
+    return new AuthenticationInterceptor();
+  }
+}

+ 56 - 0
src/main/java/com/free/SecurityConfig.java

@@ -0,0 +1,56 @@
+package com.free;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.web.SecurityFilterChain;
+
+/**
+ * Spring security的配置类
+ */
+@EnableWebSecurity
+public class SecurityConfig {
+  /**
+   * 安全过滤器,配置 URL 的安全配置
+   * <p>
+   * anyRequest | 匹配所有请求路径
+   * access | SpringEl表达式结果为true时可以访问
+   * anonymous | 匿名可以访问
+   * denyAll | 用户不能访问
+   * fullyAuthenticated | 用户完全认证可以访问(非remember-me下自动登录)
+   * hasAnyAuthority | 如果有参数,参数表示权限,则其中任何一个权限可以访问
+   * hasAnyRole | 如果有参数,参数表示角色,则其中任何一个角色可以访问
+   * hasAuthority | 如果有参数,参数表示权限,则其权限可以访问
+   * hasIpAddress | 如果有参数,参数表示IP地址,如果用户IP和参数匹配,则可以访问
+   * hasRole | 如果有参数,参数表示角色,则其角色可以访问
+   * permitAll | 用户可以任意访问
+   * rememberMe | 允许通过remember-me登录的用户访问
+   * authenticated | 用户登录后可访问
+   */
+  @Bean
+  SecurityFilterChain defaultSecurityFilterChain(HttpSecurity httpSecurity) throws Exception {
+    httpSecurity
+        // 开启跨域
+        // .cors().and()
+        // 禁用CSRF,因为不使用 session,前后端分离项目不需要
+        .csrf().disable()
+        // 禁用session,前后端分离项目基于token不需要
+        .sessionManagement(AbstractHttpConfigurer::disable)
+        .authorizeRequests(
+            authorizeRequests -> authorizeRequests.antMatchers("/**").permitAll()
+                .anyRequest().authenticated());
+    return httpSecurity.build();
+  }
+
+  /*
+   * 注入BCryptPasswordEncoder
+   */
+  @Bean
+  public BCryptPasswordEncoder bCryptPasswordEncoder() {
+    return new BCryptPasswordEncoder();
+  }
+
+}

+ 10 - 0
src/main/java/com/free/SwaggerConfig.java

@@ -0,0 +1,10 @@
+package com.free;
+
+import org.springframework.context.annotation.Configuration;
+
+import springfox.documentation.oas.annotations.EnableOpenApi;
+
+@EnableOpenApi
+@Configuration // 注入spring boot
+public class SwaggerConfig {
+}

+ 12 - 0
src/main/java/com/free/annotation/PassToken.java

@@ -0,0 +1,12 @@
+package com.free.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target({ ElementType.METHOD, ElementType.TYPE })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface PassToken {
+  boolean required() default true;
+}

+ 73 - 0
src/main/java/com/free/controller/LoginController.java

@@ -0,0 +1,73 @@
+package com.free.controller;
+
+import java.util.Map;
+
+import javax.validation.Valid;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.util.StringUtils;
+import org.springframework.web.bind.annotation.PathVariable;
+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 com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.free.dto.LoginDTO;
+import com.free.entity.system.Admin;
+import com.free.frame.CustomizationException;
+import com.free.frame.ExceptionEnum;
+import com.free.frame.ResponseFormat;
+import com.free.service.system.AdminService;
+import com.free.utils.BcryptUtil;
+import com.free.utils.JwtUtil;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+
+@RestController
+@RequestMapping("/login")
+@Api(tags = "登录服务")
+public class LoginController {
+
+  /** 管理员登录类型 */
+  private String adminLoginType = "Admin";
+  /** 客服人员登录类型 */
+  private String csLoginType = "User";
+
+  @Autowired
+  private AdminService adminService;
+
+  @ApiOperation("客服服务登录")
+  @PostMapping("/{type}")
+  public Object login(@PathVariable String type, @RequestBody @Valid LoginDTO data) {
+    Map<String, Object> userInfo = null;
+    if (this.adminLoginType.equals(type)) {
+      // 管理员service
+      QueryWrapper<Admin> qw = new QueryWrapper<>();
+      qw.select("id", "nick_name", "account", "password", "role", "is_use", "is_super");
+      qw.eq("account", data.getAccount());
+      userInfo = this.adminService.getMap(qw);
+    } else if (this.csLoginType.equals(type)) {
+      // 客服service
+    }
+    // 未根据账号信息找到用户数据,抛出异常
+    if (null == userInfo) {
+      throw new CustomizationException(ExceptionEnum.ACCOUNT_NOT_FOUND);
+    }
+    // 密码验证
+    String dbPwd = (String) userInfo.get("password");
+    boolean isSame = BcryptUtil.matchesPassword(data.getPassword(), dbPwd);
+    // 密码不一致,抛出异常
+    if (!isSame) {
+      throw new CustomizationException(ExceptionEnum.PASSWORD_ERROR);
+    }
+    userInfo.remove("password");
+    // 将用户类型也放进去
+    userInfo.put("type", type);
+    // jwt加密,返回
+    String code = JwtUtil.sign(userInfo);
+    return ResponseFormat.success(code);
+
+  }
+}

+ 14 - 4
src/main/java/com/free/controller/TestController.java

@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.free.entity.Test;
 import com.free.frame.CustomizationException;
 import com.free.frame.ExceptionEnum;
+import com.free.frame.ResponseFormat;
 import com.free.service.TestService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
@@ -23,7 +24,7 @@ public class TestController {
     @GetMapping()
     public Object list() {
         List list = testService.list();
-        return list;
+        return ResponseFormat.success(list);
     }
 
     /**
@@ -34,7 +35,7 @@ public class TestController {
     @PostMapping()
     public Object save(@RequestBody Test test) {
         boolean result = testService.save(test);
-        return result;
+        return ResponseFormat.success((result));
     }
 
     /**
@@ -48,11 +49,20 @@ public class TestController {
         QueryWrapper<Test> qw = new QueryWrapper<Test>();
         qw.eq("id", id);
         Long num = testService.count(qw);
-        System.out.println(num);
         if (num <= 0) throw new CustomizationException(ExceptionEnum.NOT_FOUND);
         testService.updateById(test);
         Test newData = testService.getById(id);
-        return newData;
+        return ResponseFormat.success(newData);
+    }
+
+    @DeleteMapping("/{id}")
+    public Object delete(@PathVariable long id) {
+        QueryWrapper<Test> qw = new QueryWrapper<Test>();
+        qw.eq("id", id);
+        Long num = testService.count(qw);
+        if (num <= 0) throw new CustomizationException(ExceptionEnum.NOT_FOUND);
+        testService.removeById(id);
+        return ResponseFormat.success();
     }
 
 

+ 78 - 0
src/main/java/com/free/controller/system/AdminController.java

@@ -0,0 +1,78 @@
+package com.free.controller.system;
+
+import javax.validation.Valid;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cglib.beans.BeanCopier;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.PathVariable;
+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 com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.free.dto.system.admin.AdminCreateDTO;
+import com.free.entity.system.Admin;
+import com.free.frame.CustomizationException;
+import com.free.frame.ExceptionEnum;
+import com.free.frame.ResponseFormat;
+import com.free.service.system.AdminService;
+import com.free.utils.BaseController;
+import com.free.utils.BcryptUtil;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+
+@RestController
+@RequestMapping("/admin")
+@Api(tags = "管理员用户")
+public class AdminController extends BaseController<AdminService> {
+  @Autowired
+  private AdminService adminService;
+
+  /** 创建数据 */
+  @ApiOperation("创建数据")
+  @PostMapping("/")
+  public Object save(@RequestBody @Valid AdminCreateDTO data) {
+    System.out.println(data);
+    // 检查账号是否重复
+    QueryWrapper<Admin> qw = new QueryWrapper<Admin>();
+    qw.eq("account", data.getAccount());
+    Long num = this.adminService.count(qw);
+    // 重复抛出异常
+    if (num > 0) {
+      throw new CustomizationException(ExceptionEnum.ACCOUNT_IS_EXIST);
+    }
+    BeanCopier copier = BeanCopier.create(AdminCreateDTO.class, Admin.class, false);
+    Admin adminData = new Admin();
+    copier.copy(data, adminData, null);
+    // 密码加密
+    String ep = BcryptUtil.encryptPassword(adminData.getPassword());
+    if (null != ep) {
+      adminData.setPassword(ep);
+    }
+    this.adminService.save(adminData);
+    // 再将数据查出来返回
+    Admin returnData = this.adminService.getById(adminData.getId());
+    return ResponseFormat.success(returnData);
+  }
+
+   /** 修改数据 */
+  @ApiOperation("修改数据")
+  @PostMapping("/{id}")
+  public Object update(@PathVariable long id, @RequestBody Admin data) {
+    QueryWrapper<Admin> qw = new QueryWrapper<Admin>();
+    qw.eq("id", id);
+    Long num = this.adminService.count(qw);
+    if (num <= 0)
+      throw new CustomizationException(ExceptionEnum.NOT_FOUND);
+    data.setId(id);
+    // 密码不在修改范围内,清空
+    data.setPassword(null);
+    this.adminService.updateById(data);
+    Object newData = this.adminService.getById(id);
+    return ResponseFormat.success(newData);
+  }
+
+}

+ 16 - 0
src/main/java/com/free/controller/system/AdminRoleController.java

@@ -0,0 +1,16 @@
+package com.free.controller.system;
+
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.free.service.system.AdminRoleService;
+import com.free.utils.BaseController;
+
+import io.swagger.annotations.Api;
+
+@RestController
+@RequestMapping("/adminRole")
+@Api(tags = "管理员用户-角色")
+public class AdminRoleController extends BaseController<AdminRoleService>{
+  /** 单独一个函数处理即可,1个用户1条数据,数据转换成json */
+}

+ 14 - 0
src/main/java/com/free/dto/LoginDTO.java

@@ -0,0 +1,14 @@
+package com.free.dto;
+
+import javax.validation.constraints.NotBlank;
+
+import lombok.Data;
+
+@Data
+public class LoginDTO {
+  @NotBlank(message = "请填写账号")
+  private String account;
+  @NotBlank(message = "请填写密码")
+  private String password;
+  
+}

+ 16 - 0
src/main/java/com/free/dto/system/admin/AdminCreateDTO.java

@@ -0,0 +1,16 @@
+package com.free.dto.system.admin;
+
+import javax.validation.constraints.NotBlank;
+
+import lombok.Data;
+
+@Data
+public class AdminCreateDTO {
+  @NotBlank(message = "请填写昵称")
+  private String nick_name;
+  @NotBlank(message = "请填写账号")
+  private String account;
+  @NotBlank(message = "请填写密码")
+  private String password;
+  private String role;
+}

+ 1 - 1
src/main/java/com/free/entity/Test.java

@@ -18,7 +18,7 @@ public class Test {
     @TableId(value = "id",type = IdType.AUTO)
     @IsKey
     @IsAutoIncrement
-    @Column(name = "id",comment = "主键")
+    @Column(name = "id",comment = "数据id")
     private Long id;
 
     @TableField(value = "name")

+ 51 - 0
src/main/java/com/free/entity/system/Admin.java

@@ -0,0 +1,51 @@
+package com.free.entity.system;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.free.utils.BaseEntity;
+import com.gitee.sunchenbin.mybatis.actable.annotation.Column;
+import com.gitee.sunchenbin.mybatis.actable.annotation.Table;
+import com.gitee.sunchenbin.mybatis.actable.annotation.Unique;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Table(name = "admin")
+@TableName(value = "admin")
+@ApiModel("管理员")
+public class Admin extends BaseEntity {
+  @Column(comment = "管理员昵称")
+  @TableField(value = "nick_name")
+  @ApiModelProperty("管理员昵称")
+  private String nick_name;
+
+  @Column(comment = "账号")
+  @Unique
+  @TableField(value = "account")
+  @ApiModelProperty("账号")
+  private String account;
+
+  @Column(comment = "密码")
+  @TableField(value = "password", select = false)
+  @ApiModelProperty("密码")
+  private String password;
+
+  @Column(comment = "角色")
+  @TableField(value = "role")
+  @ApiModelProperty("角色")
+  private String role;
+
+  @Column(comment = "是否使用", defaultValue = "0")
+  @TableField(value = "is_use")
+  @ApiModelProperty("是否使用")
+  private String is_use;
+
+  @Column(comment = "是否是超级管理员", defaultValue = "1")
+  @TableField(value = "is_super")
+  @ApiModelProperty("是否是超级管理员")
+  private String is_super;
+}

+ 26 - 0
src/main/java/com/free/entity/system/AdminRole.java

@@ -0,0 +1,26 @@
+package com.free.entity.system;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.free.utils.BaseEntity;
+import com.gitee.sunchenbin.mybatis.actable.annotation.Column;
+import com.gitee.sunchenbin.mybatis.actable.annotation.Table;
+
+import io.swagger.annotations.ApiModel;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Table(name = "adminRole")
+@TableName(value = "adminRole")
+@ApiModel("管理员-角色")
+public class AdminRole extends BaseEntity{
+  @Column(comment = "所属用户id")
+  @TableField(value = "uid")
+  private Long uid;
+
+  @Column(comment = "角色id")
+  @TableField(value = "rid")
+  private Long rid;
+}

+ 62 - 0
src/main/java/com/free/entity/system/LoginRecord.java

@@ -0,0 +1,62 @@
+package com.free.entity.system;
+
+import java.time.LocalDateTime;
+
+import org.springframework.format.annotation.DateTimeFormat;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.gitee.sunchenbin.mybatis.actable.annotation.Column;
+import com.gitee.sunchenbin.mybatis.actable.annotation.IsAutoIncrement;
+import com.gitee.sunchenbin.mybatis.actable.annotation.IsKey;
+import com.gitee.sunchenbin.mybatis.actable.annotation.Table;
+import com.gitee.sunchenbin.mybatis.actable.constants.MySqlTypeConstant;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+@Table(name = "loginRecord")
+@TableName(value = "loginRecord")
+@ApiModel("登录登记表")
+public class LoginRecord {
+  @TableId(type = IdType.AUTO) // mybatis-plus主键注解
+  @IsKey
+  @IsAutoIncrement
+  @Column(name = "id", comment = "数据id")
+  @ApiModelProperty("数据id")
+  private Long id;
+  
+  @Column(comment = "用户id")
+  @TableField(value = "user_id")
+  @ApiModelProperty("用户id")
+  private Long user_id;
+
+  @Column(comment = "用户类型")
+  @TableField(value = "type")
+  @ApiModelProperty("用户类型")
+  private String type; // Admin, User
+
+  @Column(comment = "token", type = MySqlTypeConstant.TEXT)
+  @TableField(value = "token")
+  @ApiModelProperty("token")
+  private String token;
+
+  @Column(comment = "过期时间")
+  @TableField(value = "expire_time")
+  @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
+  @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+  @ApiModelProperty("过期时间")
+  private LocalDateTime expire_time;
+
+  @Column(comment = "最后登录时间")
+  @TableField(value = "last_login_time")
+  @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
+  @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+  @ApiModelProperty("最后登录时间")
+  private LocalDateTime last_login_time;
+}

+ 58 - 0
src/main/java/com/free/frame/AuthenticationInterceptor.java

@@ -0,0 +1,58 @@
+package com.free.frame;
+
+import org.springframework.web.method.HandlerMethod;
+import org.springframework.web.servlet.HandlerInterceptor;
+import org.springframework.web.servlet.ModelAndView;
+
+import com.free.annotation.PassToken;
+import com.free.utils.JwtUtil;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.lang.reflect.Method;
+
+public class AuthenticationInterceptor implements HandlerInterceptor {
+
+  @Override
+  public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
+    // 如果不是映射到方法直接通过
+    if (!(handler instanceof HandlerMethod)) {
+      return true;
+    }
+    HandlerMethod handlerMethod = (HandlerMethod) handler;
+    Method method = handlerMethod.getMethod();
+    // 检查是否有passtoken注释,有则跳过认证
+    if (method.isAnnotationPresent(PassToken.class)) {
+      PassToken passToken = method.getAnnotation(PassToken.class);
+      if (passToken.required()) {
+        return true;
+      }
+    }
+    // 执行认证
+    String tokenHeader = request.getHeader(JwtUtil.TOKEN_HEADER);// 从 http 请求头中取出 token
+    if (tokenHeader == null) {
+      throw new CustomizationException(ExceptionEnum.TOKEN_NOT_FOUND);
+    }
+    String token = tokenHeader.replace(JwtUtil.TOKEN_PREFIX, "");
+    if (token == null) {
+      throw new CustomizationException(ExceptionEnum.TOKEN_NOT_FOUND);
+    }
+    // 1.查询数据库中该用户的时间是否超时: 如果有问题,验证函数内会抛异常
+    JwtUtil.verify(token);
+    // 2.没超时则,自动续时
+    JwtUtil.renewal(token);
+    return true;
+  }
+
+  @Override
+  public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
+      ModelAndView modelAndView) throws Exception {
+
+  }
+
+  @Override
+  public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
+      throws Exception {
+
+  }
+}

+ 35 - 30
src/main/java/com/free/frame/ExceptionEnum.java

@@ -4,33 +4,38 @@ package com.free.frame;
  * 自定义异常枚举
  */
 public enum ExceptionEnum {
-    SERVICE_FAULT("500", "服务发生错误"),
-    // 数据操作异常定义
-    NOT_FOUND("4004","数据不存在或已删除!");
-
-
-    /**
-     * 错误码
-     */
-    private final String resultCode;
-
-    /**
-     * 错误描述
-     */
-    private final String resultMsg;
-
-    ExceptionEnum(String resultCode, String resultMsg) {
-        this.resultCode = resultCode;
-        this.resultMsg = resultMsg;
-    }
-
-
-    public String getResultCode() {
-        return resultCode;
-    }
-
-
-    public String getResultMsg() {
-        return resultMsg;
-    }
-    }
+  SERVICE_FAULT("400", "服务发生错误"), //
+  VALID("4001", "参数校验失败"), // 一般只用码
+  ACCOUNT_IS_EXIST("4002", "账号已存在"),
+  ACCOUNT_NOT_FOUND("4011", "未找到账号信息"),
+  PASSWORD_ERROR("4012", "密码错误"),
+  TOKEN_NOT_FOUND("4013", "未找到登录信息"),
+  TOKEN_INVALID("4014", "登录已超时,请重新登录"),
+  TOKEN_ERROR("4015", "登录信息可能被篡改,请重新登录"),
+  NO_LOGIN_RECORD("4016", "未找到登录信息,请重新登录"),
+  // 数据操作异常定义
+  NOT_FOUND("404", "数据不存在或已删除!");
+
+  /**
+   * 错误码
+   */
+  private final String resultCode;
+
+  /**
+   * 错误描述
+   */
+  private final String resultMsg;
+
+  ExceptionEnum(String resultCode, String resultMsg) {
+    this.resultCode = resultCode;
+    this.resultMsg = resultMsg;
+  }
+
+  public String getResultCode() {
+    return resultCode;
+  }
+
+  public String getResultMsg() {
+    return resultMsg;
+  }
+}

+ 50 - 26
src/main/java/com/free/frame/GlobalExceptionController.java

@@ -2,9 +2,15 @@ package com.free.frame;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpStatus;
+import org.springframework.util.StringUtils;
+import org.springframework.validation.BindingResult;
+import org.springframework.validation.FieldError;
+import org.springframework.web.bind.MethodArgumentNotValidException;
 import org.springframework.web.bind.annotation.ControllerAdvice;
 import org.springframework.web.bind.annotation.ExceptionHandler;
 import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.ResponseStatus;
 
 import javax.servlet.http.HttpServletRequest;
 import java.io.ByteArrayOutputStream;
@@ -15,35 +21,53 @@ import java.io.PrintStream;
  */
 @ControllerAdvice
 public class GlobalExceptionController {
-    private static final Logger log = LoggerFactory.getLogger(GlobalExceptionController.class);
-    @ExceptionHandler(value = CustomizationException.class)
-    @ResponseBody
-    public ResponseFormat exception(HttpServletRequest req, CustomizationException ce) {
-        log.error("==========自定义异常捕获==========");
-        log.error(getExceptionInfo(ce));
-        return ResponseFormat.customizationError(ce);
-    }
-    @ExceptionHandler(value = Exception.class)
-    @ResponseBody
-    public ResponseFormat exceptionHandler(HttpServletRequest req, Exception e) {
-        log.error("==========全局异常捕获==========");
-        log.error(getExceptionInfo(e));
-        return ResponseFormat.error();
+  private static final Logger log = LoggerFactory.getLogger(GlobalExceptionController.class);
+
+  @ExceptionHandler(value = CustomizationException.class)
+  @ResponseBody
+  public ResponseFormat exception(HttpServletRequest req, CustomizationException ce) {
+    log.error("==========自定义异常捕获==========");
+    log.error(getExceptionInfo(ce));
+    return ResponseFormat.customizationError(ce);
+  }
+
+  @ExceptionHandler({ MethodArgumentNotValidException.class })
+  @ResponseBody
+  public ResponseFormat methodArgNotValidException(MethodArgumentNotValidException ex) {
+    BindingResult bindingResult = ex.getBindingResult();
+    StringBuilder sb = new StringBuilder("校验失败:");
+    char appendChar = ',';
+    for (FieldError fieldError : bindingResult.getFieldErrors()) {
+      sb.append(fieldError.getField()).append(":").append(fieldError.getDefaultMessage()).append(appendChar);
     }
+    String msg = sb.toString();
+    msg = StringUtils.trimTrailingCharacter(msg, appendChar);
+    System.out.println(msg);
+    ResponseFormat rf = ResponseFormat.validError();
+    rf.setErrmsg(msg);
+    return rf;
+  }
 
+  @ExceptionHandler(value = Exception.class)
+  @ResponseBody
+  public ResponseFormat exceptionHandler(HttpServletRequest req, Exception e) {
+    log.error("==========全局异常捕获==========");
+    log.error(getExceptionInfo(e));
+    return ResponseFormat.error();
+  }
 
-    private static String getExceptionInfo(Exception ex) {
-        ByteArrayOutputStream out = new ByteArrayOutputStream();
-        PrintStream printStream = new PrintStream(out);
-        ex.printStackTrace(printStream);
-        String rs = new String(out.toByteArray());
-        try {
-            printStream.close();
-            out.close();
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-        return rs;
+  private static String getExceptionInfo(Exception ex) {
+    ByteArrayOutputStream out = new ByteArrayOutputStream();
+    PrintStream printStream = new PrintStream(out);
+    ex.printStackTrace(printStream);
+    String rs = new String(out.toByteArray());
+    try {
+      printStream.close();
+      out.close();
+    } catch (Exception e) {
+      e.printStackTrace();
     }
+    return rs;
+  }
 
 }

+ 60 - 37
src/main/java/com/free/frame/ResponseFormat.java

@@ -1,50 +1,73 @@
 package com.free.frame;
 
+import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Data;
 
 /**
  * 格式化返回结果
  */
 @Data
+@Schema(description = "响应返回数据对象")
 public class ResponseFormat {
-    private String errcode;
-    private String errmsg;
-    private Object data;
+  private String errcode;
+  private String errmsg;
+  private Object data;
 
-    /**
-     * 接口返回成功
-     * @param data
-     * @return
-     */
-    public static ResponseFormat success(Object data) {
-        ResponseFormat rf = new ResponseFormat();
-        rf.setData(data);
-        rf.setErrcode("0");
-        rf.setErrmsg("ok");
-        return rf;
-    }
+  /**
+   * 接口返回成功
+   * 
+   * @param data
+   * @return
+   */
+  public static ResponseFormat success(Object data) {
+    ResponseFormat rf = new ResponseFormat();
+    rf.setData(data);
+    rf.setErrcode("0");
+    rf.setErrmsg("ok");
+    return rf;
+  }
 
-    /**
-     * 接口返回自定义异常
-     * @param e
-     * @return
-     */
-    public static ResponseFormat customizationError(CustomizationException e) {
-        ResponseFormat rf = new ResponseFormat();
-        rf.setErrmsg(e.getErrMsg());
-        rf.setErrcode(e.getErrCode());
-        return rf;
-    }
+  public static ResponseFormat success() {
+    ResponseFormat rf = new ResponseFormat();
+    rf.setErrcode("0");
+    rf.setErrmsg("ok");
+    return rf;
+  }
 
-    /**
-     * 接口返回其他异常
-     * @return
-     */
-    public static ResponseFormat error() {
-        ExceptionEnum serviceFault = ExceptionEnum.SERVICE_FAULT;
-        ResponseFormat rf = new ResponseFormat();
-        rf.setErrcode(serviceFault.getResultCode());
-        rf.setErrmsg(serviceFault.getResultMsg());
-        return rf;
-    }
+  /**
+   * 接口返回自定义异常
+   * 
+   * @param e 自定义异常
+   * @return
+   */
+  public static ResponseFormat customizationError(CustomizationException e) {
+    ResponseFormat rf = new ResponseFormat();
+    rf.setErrmsg(e.getErrMsg());
+    rf.setErrcode(e.getErrCode());
+    return rf;
+  }
+
+  /**
+   * 参数校验失败
+   * @return
+   */
+  public static ResponseFormat validError() {
+    ExceptionEnum serviceFault = ExceptionEnum.VALID;
+    ResponseFormat rf = new ResponseFormat();
+    rf.setErrcode(serviceFault.getResultCode());
+    return rf;
+  }
+
+  /**
+   * 接口返回其他异常
+   * 
+   * @return
+   */
+  public static ResponseFormat error() {
+    ExceptionEnum serviceFault = ExceptionEnum.SERVICE_FAULT;
+    ResponseFormat rf = new ResponseFormat();
+    rf.setErrcode(serviceFault.getResultCode());
+    rf.setErrmsg(serviceFault.getResultMsg());
+    return rf;
+  }
 }

+ 11 - 0
src/main/java/com/free/mapper/system/AdminMapper.java

@@ -0,0 +1,11 @@
+package com.free.mapper.system;
+
+import org.apache.ibatis.annotations.Mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.free.entity.system.Admin;
+
+@Mapper
+public interface AdminMapper extends BaseMapper<Admin>{
+  
+}

+ 11 - 0
src/main/java/com/free/mapper/system/AdminRoleMapper.java

@@ -0,0 +1,11 @@
+package com.free.mapper.system;
+
+import org.apache.ibatis.annotations.Mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.free.entity.system.AdminRole;
+
+@Mapper
+public interface AdminRoleMapper extends BaseMapper<AdminRole> {
+
+}

+ 9 - 0
src/main/java/com/free/mapper/system/LoginRecordMapper.java

@@ -0,0 +1,9 @@
+package com.free.mapper.system;
+
+import org.apache.ibatis.annotations.Mapper;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.free.entity.system.LoginRecord;
+@Mapper
+public interface LoginRecordMapper extends BaseMapper<LoginRecord>{
+  
+}

+ 12 - 0
src/main/java/com/free/service/system/AdminRoleService.java

@@ -0,0 +1,12 @@
+package com.free.service.system;
+
+import org.springframework.stereotype.Service;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.free.entity.system.AdminRole;
+import com.free.mapper.system.AdminRoleMapper;
+
+@Service
+public class AdminRoleService extends ServiceImpl<AdminRoleMapper, AdminRole> {
+
+}

+ 12 - 0
src/main/java/com/free/service/system/AdminService.java

@@ -0,0 +1,12 @@
+package com.free.service.system;
+
+import org.springframework.stereotype.Service;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.free.entity.system.Admin;
+import com.free.mapper.system.AdminMapper;
+
+@Service
+public class AdminService extends ServiceImpl<AdminMapper, Admin>{
+  
+}

+ 12 - 0
src/main/java/com/free/service/system/LoginRecordService.java

@@ -0,0 +1,12 @@
+package com.free.service.system;
+
+import org.springframework.stereotype.Service;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.free.entity.system.LoginRecord;
+import com.free.mapper.system.LoginRecordMapper;
+
+@Service
+public class LoginRecordService extends ServiceImpl<LoginRecordMapper, LoginRecord> {
+
+}

+ 111 - 0
src/main/java/com/free/utils/BaseController.java

@@ -0,0 +1,111 @@
+package com.free.utils;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestParam;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.free.entity.system.Admin;
+import com.free.frame.CustomizationException;
+import com.free.frame.ExceptionEnum;
+import com.free.frame.ResponseFormat;
+
+import io.swagger.annotations.ApiOperation;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+
+@SuppressWarnings("rawtypes")
+public class BaseController<K extends ServiceImpl> {
+  @Autowired
+  private K service;
+
+  /** 列表查询 */
+  @ApiOperation("查询数据列表")
+  @SuppressWarnings({ "unchecked" })
+  @GetMapping()
+  public Object list(@RequestParam Map<String, Object> allParams) {
+    Long skip = null, limit = null;
+    Map map = new HashMap();
+    QueryWrapper qw = new QueryWrapper<>();
+    /** 参数处理处理 */
+    for (String key : allParams.keySet()) {
+      Object value = allParams.get(key);
+      if (key.equals("skip")) {
+        skip = Long.valueOf(String.valueOf(value));
+      } else if (key.equals("limit")) {
+        limit = Long.valueOf(String.valueOf(value));
+      } else {
+        // 其他为查询条件
+        qw.eq(key, value);
+      }
+    }
+    /** 分页处理 */
+    if (null != skip && null != limit) {
+      IPage page = new Page<>(skip, limit);
+      IPage pageResult = service.page(page, qw);
+      List data = pageResult.getRecords();
+      long total = pageResult.getTotal();
+      map.put("data", data);
+      map.put("total", total);
+    } else {
+      List list = service.list(qw);
+      map.put("data", list);
+    }
+    return ResponseFormat.success(map);
+  }
+  /** 根据id查询 */
+  @ApiOperation("查询数据")
+  @GetMapping("/{id}")
+  public Object fetch(@PathVariable long id) {
+    Object newData = service.getById(id);
+    return ResponseFormat.success(newData);
+  }
+
+  /** 创建数据 */
+  @ApiOperation("创建数据")
+  @SuppressWarnings("unchecked")
+  // @PostMapping()
+  public Object save(@RequestBody Admin data) {
+    boolean result = service.save(data);
+    return ResponseFormat.success(result);
+  }
+
+  /** 修改数据 */
+  @ApiOperation("修改数据")
+  @SuppressWarnings({ "unchecked" })
+  @PostMapping("/{id}")
+  public Object update(@PathVariable long id, @RequestBody Admin data) {
+    QueryWrapper qw = new QueryWrapper<>();
+    qw.eq("id", id);
+    Long num = service.count(qw);
+    if (num <= 0)
+      throw new CustomizationException(ExceptionEnum.NOT_FOUND);
+    service.updateById(data);
+    Object newData = service.getById(id);
+    return ResponseFormat.success(newData);
+  }
+
+  /** 根据id删除数据 */
+  @ApiOperation("删除数据")
+  @SuppressWarnings({ "unchecked" })
+  @DeleteMapping("/{id}")
+  public Object delete(@PathVariable long id) {
+    QueryWrapper qw = new QueryWrapper<>();
+    qw.eq("id", id);
+    Long num = service.count(qw);
+    if (num <= 0)
+      throw new CustomizationException(ExceptionEnum.NOT_FOUND);
+    service.removeById(id);
+    return ResponseFormat.success();
+  }
+}

+ 32 - 9
src/main/java/com/free/utils/BaseEntity.java

@@ -1,16 +1,39 @@
 package com.free.utils;
 
+import java.time.LocalDateTime;
+
+import org.springframework.format.annotation.DateTimeFormat;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
 import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableId;
-import lombok.Getter;
-import lombok.Setter;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.gitee.sunchenbin.mybatis.actable.annotation.Column;
+import com.gitee.sunchenbin.mybatis.actable.annotation.IsAutoIncrement;
+import com.gitee.sunchenbin.mybatis.actable.annotation.IsKey;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
 
-@Getter
-@Setter
+@Data
 public class BaseEntity {
-    @TableId(type = IdType.AUTO) //mybatis-plus主键注解
-//    @IsKey 						 //actable主键注解
-//    @IsAutoIncrement			 //自增
-//    @Column 					 //对应数据库字段,不配置name会直接采用属性名作为字段名
-    private Long id;
+  @TableId(type = IdType.AUTO) // mybatis-plus主键注解
+  @IsKey
+  @IsAutoIncrement
+  @Column(name = "id", comment = "数据id")
+  @ApiModelProperty("数据id")
+  private Long id;
+
+  @TableField(fill = FieldFill.INSERT)
+  @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
+  @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+  @ApiModelProperty("数据创建时间")
+  private LocalDateTime create_time;
+
+  @TableField(fill = FieldFill.UPDATE)
+  @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
+  @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+  @ApiModelProperty("数据修改时间")
+  private LocalDateTime update_time;
 }

+ 24 - 0
src/main/java/com/free/utils/BcryptUtil.java

@@ -0,0 +1,24 @@
+package com.free.utils;
+
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+
+public class BcryptUtil {
+  /**
+   * 生成密码
+   * @param password 输入的密码
+   */
+  public static String encryptPassword(String password) {
+    BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
+    return passwordEncoder.encode(password);
+  }
+  /**
+   * 校验密码
+   * @param rPassword 输入的密码
+   * @param ePassword 数据库中加密的密码
+   */
+  public static boolean matchesPassword(String rPassword, String ePassword) {
+    BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
+    return passwordEncoder.matches(rPassword, ePassword);
+  }
+
+}

+ 202 - 0
src/main/java/com/free/utils/JwtUtil.java

@@ -0,0 +1,202 @@
+package com.free.utils;
+
+import com.auth0.jwt.JWTVerifier;
+import com.auth0.jwt.algorithms.Algorithm;
+import com.auth0.jwt.interfaces.Claim;
+import com.auth0.jwt.interfaces.DecodedJWT;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.free.entity.system.LoginRecord;
+import com.free.frame.CustomizationException;
+import com.free.frame.ExceptionEnum;
+import com.free.mapper.system.LoginRecordMapper;
+import com.free.service.system.LoginRecordService;
+import com.auth0.jwt.JWT;
+import com.auth0.jwt.JWTCreator;
+
+import java.time.LocalDateTime;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.annotation.PostConstruct;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+@Component
+public class JwtUtil {
+  public static final String TOKEN_HEADER = "Authorization";
+  public static final String TOKEN_PREFIX = "Free ";
+  // 过期时间, 半个小时
+  private static final long EXPIRE_MIN = 30;
+  private static final long EXPIRE_TIME = EXPIRE_MIN * 60 * 1000;
+  // 密钥
+  private static final String SECRET = "Ziyouyanfa!@#";
+  /** 特殊处理的key,id.因为是long类型 */
+  private static String spcialKey = "id";
+
+  @Autowired
+  private LoginRecordService lrs;
+
+  private static JwtUtil jwtUtil = new JwtUtil();
+
+  @PostConstruct
+  public void init() {
+    jwtUtil = this;
+    jwtUtil.lrs = this.lrs;
+  }
+
+  /**
+   * 生成签名
+   *
+   * @param map    数据
+   * @param secret 密码
+   * @return 加密后的token
+   */
+  public static String sign(Map<String, Object> map) {
+    Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
+    Algorithm algorithm = Algorithm.HMAC256(SECRET); // 使用HS256算法
+    JWTCreator.Builder builder = JWT.create();
+
+    for (Map.Entry<String, Object> entry : map.entrySet()) {
+      String k = entry.getKey();
+      if (spcialKey.equals(k)) {
+        Long v = (Long) entry.getValue();
+        builder.withClaim(k, v);
+      } else {
+        String v = (String) entry.getValue();
+        builder.withClaim(k, v);
+      }
+    }
+    String token = builder.withExpiresAt(date)
+        .sign(algorithm);
+    StringBuilder sb = new StringBuilder();
+    sb.append(TOKEN_PREFIX);
+    sb.append(token);
+    String returnToken = sb.toString();
+    // 创建登录数据
+    createLoginRecord(token);
+    return returnToken;
+  }
+
+  /**
+   * 创建登录数据
+   * 
+   * @param token jwt
+   */
+  public static void createLoginRecord(String token) {
+    Map<String, Object> map = getDetails(token);
+    Long user_id = (Long) map.get("id");
+    String type = (String) map.get("type");
+    // 第一次登录是创建,以后都是更新, 每个用户都有1个登录数据,但是能否使用要看过期时间
+    QueryWrapper<LoginRecord> qw = new QueryWrapper<>();
+    // qw.eq("user_id", user_id);
+    // qw.eq("type", type);
+    Map<String,Object> qm = new HashMap<>();
+    qm.put("user_id",user_id);
+    qm.put("type",type);
+    qw.allEq(qm);
+    LoginRecord histroy = jwtUtil.lrs.getOne(qw);
+    LocalDateTime last_login_time = LocalDateTime.now();
+    LocalDateTime expire_time = LocalDateTime.now().plusMinutes(EXPIRE_MIN);
+    if (null == histroy) {
+      // 创建数据
+      LoginRecord lr = new LoginRecord();
+      lr.setExpire_time(expire_time);
+      lr.setLast_login_time(last_login_time);
+      lr.setToken(token);
+      lr.setType(type);
+      lr.setUser_id(user_id);
+      jwtUtil.lrs.save(lr);
+    } else {
+      // 修改数据
+      LoginRecord lr = new LoginRecord();
+      lr.setExpire_time(expire_time);
+      lr.setLast_login_time(last_login_time);
+      lr.setToken(token);
+      lr.setId(histroy.getId());
+      jwtUtil.lrs.updateById(lr);
+    }
+  }
+
+  /**
+   * token续期
+   * 
+   * @param token jwt
+   */
+  public static void renewal(String token) {
+    Map<String, Object> map = getDetails(token);
+    Long user_id = (Long) map.get("id");
+    String type = (String) map.get("type");
+    LocalDateTime expire_time = LocalDateTime.now().plusMinutes(EXPIRE_MIN);
+    QueryWrapper<LoginRecord> qw = new QueryWrapper<>();
+    qw.eq("user_id", user_id).eq("type", type);
+    LoginRecord histroy = jwtUtil.lrs.getOne(qw);
+    if (null == histroy) {
+      // 没有数据,转至创建
+      createLoginRecord(token);
+    } else {
+      // 修改过期时间
+      LoginRecord lr = new LoginRecord();
+      lr.setExpire_time(expire_time);
+      lr.setId(histroy.getId());
+      jwtUtil.lrs.updateById(lr);
+    }
+  }
+
+  /**
+   * 校验token是否正确
+   *
+   * @param token 令牌
+   * @return 是否正确
+   */
+  public static void verify(String token) {
+    try {
+      // jwt先解密,如果解不开,那就不用往后继续查时间的问题了
+      JWT.require(Algorithm.HMAC256(SECRET)).build().verify(token);
+    } catch (Exception e) {
+      throw new CustomizationException(ExceptionEnum.TOKEN_ERROR);
+    }
+    // 再解开token,查看数据
+    Map<String, Object> map = getDetails(token);
+    Long user_id = (Long) map.get("id");
+    String type = (String) map.get("type");
+    QueryWrapper<LoginRecord> qw = new QueryWrapper<>();
+    qw.eq("user_id", user_id).eq("type", type);
+    LoginRecord histroy = jwtUtil.lrs.getOne(qw);
+    if (null == histroy) {
+      throw new CustomizationException(ExceptionEnum.NO_LOGIN_RECORD);
+    }
+    // 取出过期时间,和当前时间进行比较
+    LocalDateTime nowTime = LocalDateTime.now();
+    boolean is_before = histroy.getExpire_time().isBefore(nowTime);
+    if (!is_before) {
+      throw new CustomizationException(ExceptionEnum.TOKEN_INVALID);
+    }
+  }
+
+  /**
+   * 获得token中的信息
+   *
+   * @return token中包含的名称
+   */
+  public static Map<String, Object> getDetails(String token) {
+    try {
+      DecodedJWT jwt = JWT.decode(token);
+      Map<String, Claim> map = jwt.getClaims();
+      Map<String, Object> returnMap = new HashMap<>();
+      for (Map.Entry<String, Claim> entry : map.entrySet()) {
+        String k = entry.getKey();
+        if (spcialKey.equals(k)) {
+          Long v = jwt.getClaim(k).asLong();
+          returnMap.put(k, v);
+        } else {
+          String v = jwt.getClaim(k).asString();
+          returnMap.put(k, v);
+        }
+      }
+      return returnMap;
+    } catch (Exception e) {
+      return null;
+    }
+  }
+}

+ 8 - 2
src/main/resources/application.yml

@@ -4,12 +4,17 @@ server:
 spring:
   datasource:
     driver-class-name: com.mysql.cj.jdbc.Driver
-    url: jdbc:mysql://localhost:3306/customer?serverTimezone=GMT%2B8
+    url: jdbc:mysql://192.168.1.153:3306/customer?serverTimezone=GMT%2B8
     username: root
     password: root
   devtools:
     restart:
       enabled: true
+  mvc:
+    pathmatch:
+      matching-strategy: ANT_PATH_MATCHER
+  autoconfigure:
+    exclude: org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration
 
 mybatis:
   table:
@@ -23,7 +28,8 @@ mybatis-plus:
   mapper-locations: classpath*:com/gitee/sunchenbin/mybatis/actable/mapping/*/*.xml
 #  type-aliases-package: com.free.entity
   configuration:
-    map-underscore-to-camel-case: true
+    map-underscore-to-camel-case: false
+    call-setters-on-nulls: true
     log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
 logging:
   level: