Spring Boot+Restful API+RedisCache+Swagger UI+JWT+Mybatis

1.主要目标

1.Swagger UI展示接口文档
2.RedisCache缓存查询结果,减轻查询数据库压力
3.接口调用使用Token认证

2.环境及版本

Win10
Spring Boot v2.7.1
Springfox-swagger2 2.9.2
Springfox-swagger-ui 2.9.2
Java-jwt 3.8.3

3.项目结构

在这里插入图片描述

4.POM文件

项目根目录下的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.1-SNAPSHOT</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</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>

            <optional>true</optional>
        </dependency>
        <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.1.0</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <!--			<version>8.0.11</version>-->
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.9.2</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.9.2</version>
        </dependency>
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.8.3</version>
        </dependency>
        <dependency>
            <groupId>com.vaadin.external.google</groupId>
            <artifactId>android-json</artifactId>
            <version>0.0.20131108.vaadin1</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>net.sf.json-lib</groupId>
            <artifactId>json-lib</artifactId>
            <version>2.2.3</version>
            <classifier>jdk15</classifier>
            <exclusions>
                <exclusion>
                    <artifactId>ezmorph</artifactId>
                    <groupId>net.sf.ezmorph</groupId>
                </exclusion>
                <exclusion>
                    <artifactId>commons-beanutils</artifactId>
                    <groupId>commons-beanutils</groupId>
                </exclusion>
            </exclusions><!-- 指定jdk版本 -->
        </dependency>
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper-spring-boot-starter</artifactId>
            <version>1.4.1</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.8.8</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.8</version>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>commons-beanutils</groupId>
            <artifactId>commons-beanutils</artifactId>
            <version>1.9.4</version>
        </dependency>
        <dependency>
            <groupId>commons-httpclient</groupId>
            <artifactId>commons-httpclient</artifactId>
            <version>3.1</version>
        </dependency>
        <dependency>
            <groupId>net.sf.ezmorph</groupId>
            <artifactId>ezmorph</artifactId>
            <version>1.0.6</version>
        </dependency>


    </dependencies>

    <build>
        <defaultGoal>compile</defaultGoal>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
    <repositories>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
        <repository>
            <id>spring-snapshots</id>
            <name>Spring Snapshots</name>
            <url>https://repo.spring.io/snapshot</url>
            <releases>
                <enabled>false</enabled>
            </releases>
        </repository>
    </repositories>
    <pluginRepositories>
        <pluginRepository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </pluginRepository>
        <pluginRepository>
            <id>spring-snapshots</id>
            <name>Spring Snapshots</name>
            <url>https://repo.spring.io/snapshot</url>
            <releases>
                <enabled>false</enabled>
            </releases>
        </pluginRepository>
    </pluginRepositories>

</project>


添加这几个依赖,解决JSONObject报错

在这里插入图片描述

5.创建实体模型

以下实体模型均实现了Serializable 序列化接口

在这里插入图片描述

用户模型:UserDomain.java

package com.example.demo.domain;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import java.io.Serializable;
/**
 * 用户实体模型,提供属性赋值和获取
 */
public class UserDomain implements Serializable{
    private Integer id;
    private String user;
    private String password;

    public String getId(){
        return id.toString();
    }
    public void setId(String id){
        this.id=Integer.valueOf(id);
    }
    public String getUser(){
        return user;
    }
    public void setUser(String user){
        this.user=user;
    }
    public String getPassword(){
        return password;
    }
    public void setPassword(String password){
        this.password=password;
    }
    /**
     * @param password 明文密码
     * 将加密后的密文赋值到实体属性
     */
    public void setEncodePassword(String password) {
        BCryptPasswordEncoder bCryptPasswordEncoder=new BCryptPasswordEncoder();
        this.password = bCryptPasswordEncoder.encode(password);
    }

    /**
     * @param rawPassword 明文密码
     * @return 校验结果
     */
    public boolean checkPassword(String rawPassword){
        BCryptPasswordEncoder bCryptPasswordEncoder=new BCryptPasswordEncoder();
        return bCryptPasswordEncoder.matches(rawPassword,password);
    }
}

成绩模型:ScoreDomain.java

package com.example.demo.domain;

import java.io.Serializable;

/**
 * 成绩实体模型
 */
public class ScoreDomain implements Serializable {
    private Integer id;
    private String sid;
    private float score;

    public String getId(){
        return id.toString();
    }
    public void setId(String id){
        this.id=Integer.valueOf(id);
    }

    public String getSid(){
        return sid;
    }
    public void setSid(String sid){
        this.sid=sid;
    }
    public String getScore(){
        return String.valueOf(score);
    }
    public void setScore(String score){
        this.score=Float.parseFloat(score);
    }
}

6.创建Mapper接口

将SQL语句和实体模型绑定,实现字段赋值和查询

在这里插入图片描述

ScoreMapper接口

package com.example.demo.mapper;

import com.example.demo.domain.ScoreDomain;
import org.apache.ibatis.annotations.*;

import java.util.List;
//import org.apache.tomcat.jni.User;

/**
 * 将模型与SQL绑定,实现增删改查
 */
@Mapper
public interface ScoreMapper {
    @Select("select * from sc where id=#{id}")
    ScoreDomain getScoreById(Integer id);

    @Insert("insert into sc(sid,score) values(#{sid},#{score})")
    int insert(ScoreDomain score);

    @Update("update sc set sid=#{sid},score=#{score} where id=#{id}")
    int update(ScoreDomain score);

    @Delete("delete from sc where id=#{id}")
    int delete(Integer id);

    @Select("select * from sc")
    List<ScoreDomain> list();
}

UserMapper接口

package com.example.demo.mapper;

import com.example.demo.domain.UserDomain;
import org.apache.ibatis.annotations.*;

import java.util.List;

/**
 * 将模型与SQL绑定,实现增删改查
 */
@Mapper
public interface UserMapper {
    @Select("select * from user where id=#{id}")
    UserDomain getUserById(Integer id);

    @Select("select * from user where user=#{user}")
    UserDomain getUserByUser(String user);

    @Insert("insert into user (user,password) values(#{user},#{password})")
    int insert(UserDomain userDomain);

    @Update("update user set user=#{user},password=#{password} where id=#{id}")
    int update(UserDomain userDomain);

    @Delete("delete from user where id=#{id}")
    int delete(Integer id);

    @Select("select * from user")
    List<UserDomain> list();
}


7.创建Service类

在这里插入图片描述

创建接口ScoreInterface.java,规范Service实现类

package com.example.demo.service;
import com.example.demo.domain.ScoreDomain;

import java.util.List;

public interface ScoreInterface {
    int insert(ScoreDomain scoredomain);
    int update(ScoreDomain scoredomain);
    int delete(String id);
    ScoreDomain get(String id);
    List<ScoreDomain> list(String page,String limit);
}

创建成绩的Service实现类ScoreService.java

package com.example.demo.service;

import com.example.demo.domain.ScoreDomain;
import com.example.demo.mapper.ScoreMapper;
import com.github.pagehelper.PageHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * 调用Mapper,实现成绩模型增删改查
 */
@Service
public class ScoreService implements ScoreInterface {
    @Autowired
    private ScoreMapper scoremapper;

    @Override
    public int insert(ScoreDomain scoredomain) {
        return scoremapper.insert(scoredomain);
    }

    @Override
    public int update(ScoreDomain scoredomain) {
        return scoremapper.update(scoredomain);
    }

    @Override
    public int delete(String id) {

        return scoremapper.delete(Integer.valueOf(id));
    }

    @Override
    public ScoreDomain get(String id) {
        return scoremapper.getScoreById(Integer.valueOf(id));
    }

    @Override
    public List<ScoreDomain> list(String page,String limit) {
        PageHelper.startPage(Integer.parseInt(page),Integer.parseInt(limit));   //开启分页,必须放在查询语句前
        return scoremapper.list();
    }
}

创建接口UserInterface.java,规范Service实现类

package com.example.demo.service;

import com.example.demo.domain.UserDomain;

import java.util.List;

public interface UserInterface {
    UserDomain getUserById(String id);
    UserDomain getUserByUser(String user);
    Object insert(UserDomain userDomain);
    int update(UserDomain userDomain);
    int delete(String id);
    List<UserDomain> list(String page, String limit);
}


创建用户的Service实现类UserService.java

package com.example.demo.service;

import com.example.demo.domain.UserDomain;
import com.example.demo.mapper.UserMapper;
import com.github.pagehelper.PageHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * 运用Mapper实现用户模型查询
 */
@Service
public class UserService implements UserInterface {
    @Autowired
    private UserMapper userMapper;

    /**
     * @param id 主键id
     * @return 用户实体
     * 根据主键id查询用户实体
     */
    @Override
    public UserDomain getUserById(String id) {
        return userMapper.getUserById(Integer.valueOf(id));
    }

    /**
     * @param user 主键user
     * @return 用户实体
     * 根据主键user查询用户实体
     */
    @Override
    public UserDomain getUserByUser(String user) {
        return userMapper.getUserByUser(user);
    }

    /**
     * @param userDomain 用户实体
     * @return 序列化后的用户实体
     */
    @Override
    public Object insert(UserDomain userDomain) {
        if (userMapper.getUserByUser(userDomain.getUser()) != null) {
            return new ResponseEntity<>(HttpStatus.CREATED);
        }
        if (userMapper.insert(userDomain) == 1) {
            return userMapper.getUserByUser(userDomain.getUser());
        } else {
            return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
        }

    }

    /**
     * @param userDomain 用户实体
     * @return 更新结果条数
     */
    @Override
    public int update(UserDomain userDomain) {
        return userMapper.update(userDomain);
    }

    @Override
    public int delete(String id) {
        return userMapper.delete(Integer.valueOf(id));
    }

    /**
     * @param page  页码
     * @param limit 每页内容数量
     * @return 用户实体列表
     */
    @Override
    public List<UserDomain> list(String page, String limit) {
        PageHelper.startPage(Integer.parseInt(page), Integer.parseInt(limit));   //开启分页,必须放在查询语句前
        return userMapper.list();
    }
}


8.1创建Controller

在这里插入图片描述

创建UserController.java

package com.example.demo.controller;

import com.example.demo.annotation.LoginToken;
import com.example.demo.domain.ScoreDomain;
import com.example.demo.domain.UserDomain;
import com.example.demo.service.UserService;
import com.example.demo.util.GetCheckToken;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import net.sf.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.Map;

@CacheConfig(cacheNames = "UserController")    //设置Redis中key的前缀
@RestController
public class UserController {
    @Autowired
    UserService userService;
    @Autowired
    GetCheckToken tokenService;

    //登录
    //覆盖注入配置

    @ApiImplicitParams({
            @ApiImplicitParam(name = "token", value = "非必须token", required = false, dataType = "String", paramType = "header")})
    @PostMapping("/login")
    public Object login(@RequestBody Map<String,String> params) {
        JSONObject jsonObject = new JSONObject();
        UserDomain userDomain = userService.getUserByUser(params.get("user"));
        if (userDomain == null) {
            jsonObject.put("message", "登录失败,用户不存在");    //在正式环境中不应该提示用户不存在
            return jsonObject;
        } else {
            if (! userDomain.checkPassword(params.get("password"))) {
                jsonObject.put("message", "登录失败,密码错误");
                return jsonObject;
            } else {
                String token = tokenService.getToken(userDomain);
                jsonObject.put("token", token);     //用户验证合法后发放token
//                jsonObject.put("user", userDomain);
                return jsonObject;
            }
        }
    }

    @ApiImplicitParams({
            @ApiImplicitParam(name = "token", value = "非必须token", required = false, dataType = "String", paramType = "header")})
    @PostMapping("/register")
    public Object register(@RequestBody Map<String,String> params) {
        UserDomain userDomain = new UserDomain();
        userDomain.setUser(params.get("user"));
        userDomain.setEncodePassword(params.get("password"));
        return userService.insert(userDomain);
    }

    @LoginToken
    @ApiOperation(value = "用户列表")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "page", value = "页码", required = false, dataType = "String"),
            @ApiImplicitParam(name = "limit", value = "每页内容数量", required = false, dataType = "String")})
    @GetMapping("/users")
    @Cacheable(keyGenerator = "selfKeyGenerate")    //添加此注解可缓存查询结果
    public List<UserDomain> list(@RequestParam("page") String page, @RequestParam("limit") String limit) {

        return userService.list(page, limit);
    }

    @LoginToken
    @ApiOperation(value = "获取单个用户")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "id", value = "主键", required = true, dataType = "String"),
    })
    @GetMapping("/users/{id}")
    public UserDomain getObject(@PathVariable String id) {
        return userService.getUserById(id);
    }

    @LoginToken
    @ApiOperation(value = "修改用户")
    @ApiImplicitParams({

            @ApiImplicitParam(name = "id", value = "主键", required = true, dataType = "String")
    })
    @PutMapping("/users/{id}")
    public int updateObject(@RequestBody Map<String,String> params,@PathVariable String id) {
        UserDomain userDomain = new UserDomain();
        userDomain.setUser(params.get("user"));
        userDomain.setId(id);
        userDomain.setEncodePassword(params.get("password"));
        return userService.update(userDomain);
    }

    @LoginToken
    @ApiOperation(value = "删除用户")
    @ApiImplicitParams({@ApiImplicitParam(name = "id", value = "主键", required = true, dataType = "String"),
    })
    @DeleteMapping("/users/{id}")
    public int deleteObject(@PathVariable String id) {
        return userService.delete(id);
    }
}


创建ScoreController.java

package com.example.demo.controller;

import com.example.demo.annotation.LoginToken;
import com.example.demo.domain.ScoreDomain;
import com.example.demo.service.ScoreService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.Map;

@Api
@RestController
@CacheConfig(cacheNames = "ScoreController")    //设置Redis中key的前缀
@Component
public class ScoreController {
    @Autowired
    private ScoreService scoreService;
    

    @LoginToken
    @ApiOperation(value = "成绩列表")
    @ApiImplicitParams({@ApiImplicitParam(name = "page", value = "页码", required = false, dataType = "String"),
            @ApiImplicitParam(name = "limit", value = "每页内容数量", required = false, dataType = "String")})
    @GetMapping("/scores")
    @Cacheable(keyGenerator = "selfKeyGenerate")    //添加此注解可缓存查询结果
    public List<ScoreDomain> list(@RequestParam("page") String page, @RequestParam("limit") String limit) {

        return scoreService.list(page, limit);
    }

    @LoginToken
    @ApiOperation(value = "添加成绩")
    @PostMapping("/scores")
    public int create(@RequestBody Map<String, String> params) {
        ScoreDomain scoreDomain = new ScoreDomain();
        scoreDomain.setSid(params.get("sid"));
        scoreDomain.setScore(params.get("score"));
        return scoreService.insert(scoreDomain);
    }

    @LoginToken
    @ApiOperation(value = "获取成绩")
    @ApiImplicitParams({@ApiImplicitParam(name = "id", value = "主键", required = true, dataType = "String"),
    })
    @GetMapping("/scores/{id}")
    public ScoreDomain getObject(@PathVariable String id) {
        return scoreService.get(id);
    }

    @LoginToken
    @ApiOperation(value = "修改成绩")
    @ApiImplicitParams({@ApiImplicitParam(name = "id", value = "主键", required = true, dataType = "String")
    })
    @PutMapping("/scores/{id}")
    public int updateObject(@RequestBody Map<String, String> params, @PathVariable String id) {
        ScoreDomain scoreDomain = new ScoreDomain();
        scoreDomain.setId(id);
        scoreDomain.setSid(params.get("sid"));
        scoreDomain.setScore(params.get("score"));
        return scoreService.update(scoreDomain);
    }

    @LoginToken
    @ApiOperation(value = "删除成绩")
    @ApiImplicitParams({@ApiImplicitParam(name = "id", value = "主键", required = true, dataType = "String"),
    })
    @DeleteMapping("/scores/{id}")
    public int deleteObject(@PathVariable String id) {
        return scoreService.delete(id);
    }
}




8.2自定义Redis生成key

在这里插入图片描述

创建SelfKeyGenerate.java

package com.example.demo.util;

import io.netty.util.internal.StringUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Arrays;

@Component()
public class SelfKeyGenerate implements KeyGenerator {
    @Autowired
    private HttpServletRequest httpServletRequest;

    @Override
    public Object generate(Object target, Method method, Object... params) {
        String[] stringArray = new String[params.length];
        for (int i = 0; i < params.length; i++) {
            stringArray[i] = params[i].toString();
//            stringBuilder.append(',');
        }
        CharSequence stringParams = StringUtil.join(",", Arrays.asList(stringArray));
        String key = String.format("%s#%s(%s[%s])", target.getClass().getSimpleName(), method.getName(), httpServletRequest.getServletPath(), stringParams);
        return key;
    }
}

9.创建token获取和检查类

路径:

在这里插入图片描述

创建GetCheckToken.java类,封装token的获取和校验

package com.example.demo.util;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.example.demo.domain.UserDomain;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import org.springframework.stereotype.Service;

import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.Date;

@Service
@ApiModel
public class GetCheckToken {
    @ApiModelProperty("盐")
    private static final String SALT_KEY = "links";

    @ApiModelProperty("令牌有效期毫秒")
    private static final long TOKEN_VALIDITY = 86400000;

    @ApiModelProperty("签发主体")
    private static final String ISSUER = "alibaba";

    @ApiModelProperty("Base64 密钥")
    private final static String SECRET_KEY = Base64.getEncoder().encodeToString(SALT_KEY.getBytes(StandardCharsets.UTF_8));     //加盐的秘钥


    /**
     * 在请求API时检查token的有无和合法性
     */
    public void checkToken(String token) {
        if (token == null) {
            throw new RuntimeException("无token,请重新登录");
        }
        JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(SECRET_KEY)).build();
        try {
            jwtVerifier.verify(token);
        } catch (JWTVerificationException e) {
            throw new RuntimeException("401");
        }
    }

    /**
     * 1.根据主题,签发主体,签发时间,过期时间,以及加盐的秘钥生成token
     */
    public String getToken(UserDomain userDomain) {
        Date validity = new Date((new Date()).getTime() + TOKEN_VALIDITY);
        return JWT.create().withSubject(userDomain.getId()).withIssuer(ISSUER).withExpiresAt(validity).withIssuedAt(new Date()).sign(Algorithm.HMAC256(SECRET_KEY));

    }

}

10.创建身份认证拦截器

路径:

在这里插入图片描述

创建AuthenticationInterceptor.java拦截器,拦截需要校验token的API请求

package com.example.demo.util;

import com.example.demo.annotation.LoginToken;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;

/**
 * 1.对LoginToken注解的API拦截校验token
 */
@Component
public class AuthenticationInterceptor implements HandlerInterceptor {

    @Resource
    private GetCheckToken getCheckToken;


    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception {
        String token = httpServletRequest.getHeader("token");// 从 http 请求头中取出 token
        // 如果不是映射到方法直接通过
        if (!(object instanceof HandlerMethod)) {
            return true;
        }
        HandlerMethod handlerMethod = (HandlerMethod) object;
        Method method = handlerMethod.getMethod();

        //检查有没有需要用户权限的注解
        if (method.isAnnotationPresent(LoginToken.class)) {
            LoginToken userLoginToken = method.getAnnotation(LoginToken.class);
            if (userLoginToken.required()) {
                // 执行认证
                getCheckToken.checkToken(token);
            }
        }
        return true;
    }


    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest,
                                HttpServletResponse httpServletResponse,
                                Object o, Exception e) throws Exception {
    }
}

11.将拦截器注册到MVC配置中

路径:

在这里插入图片描述

创建InterceptorConfig.java, 将身份认证拦截器注册到配置中

package com.example.demo.config;

import com.example.demo.util.AuthenticationInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import javax.annotation.Resource;

@Configuration //此注解将此配置注册到配置中
public class InterceptorConfig implements WebMvcConfigurer {
    @Resource
    private AuthenticationInterceptor authenticationInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(authenticationInterceptor)
                .addPathPatterns("/**");    // 拦截所有请求判断有无需要校验token的注解
    }
    
}

12.定义需要校验token的注解

路径:

在这里插入图片描述

定义注解LoginToken.java

package com.example.demo.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 LoginToken {
    boolean required() default true;
}

13.将API请求结果分页的配置注入到配置中

在这里插入图片描述

创建PageHelperConfig.java,将配置注入

package com.example.demo.config;

import com.github.pagehelper.PageHelper;
import org.springframework.context.annotation.Configuration;

import java.util.Properties;

@Configuration
public class PageHelperConfig {

    public PageHelper getPageHelper() {
        PageHelper pageHelper = new PageHelper();
        Properties properties = new Properties();
        properties.setProperty("helperDialect", "mysql");
        properties.setProperty("reasonable", "true");
        properties.setProperty("supportMethodsArguments", "true");
        properties.setProperty("params", "count=countSql");
        pageHelper.setProperties(properties);
        return pageHelper;
    }
}

14.注入Swagger UI的配置

在这里插入图片描述

定义SwaggerConfig.java注入配置

package com.example.demo.config;

import io.swagger.annotations.Api;
//import io.swagger.annotations.Contact;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.service.Parameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

import java.util.ArrayList;
import java.util.List;


@Configuration
@EnableSwagger2
public class SwaggerConfig {


    /**
     * 给每个API设置必要请求头token,登录的API不应该设置,已在LoginController.java做了覆盖处理
     */
    @Bean
    List<Parameter> setHeaderToken() {
        List<Parameter> pars = new ArrayList<>();
        ParameterBuilder parameterBuilder = new ParameterBuilder();
        parameterBuilder.name("token").description("用户TOKEN").modelRef(new ModelRef("string")).parameterType("header")
                .required(true).build();
        pars.add(parameterBuilder.build());
        return pars;
    }

    @Bean
    public Docket getDocket() {

        // 创建封面信息对象
        ApiInfoBuilder apiInfoBuilder = new ApiInfoBuilder();

        // 设置文档标题、描述、联系人
        apiInfoBuilder.title("平台接口说明文档")
                .description("此文档说明了平台后端接口规范")
                .version("v1.0.0")
                .contact(new Contact("CHEN", "http://127.0.0.1:8080/scores", "secret@qq.com"));

        ApiInfo apiInfo = apiInfoBuilder.build();

        // 指定文档风格
        Docket docket = new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo) // 指定生成的文档中的封面信息;文档标题、版本、作者
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.example.demo.controller"))
                .paths(PathSelectors.any())
                .build().globalOperationParameters(setHeaderToken());

        return docket;
    }

}

15.1项目配置

在这里插入图片描述

项目配置application.properties

spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.url=jdbc:mysql://192.168.8.140:3306/test?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.mvc.pathmatch.matching-strategy=ANT_PATH_MATCHER
#redis连接配置
spring.redis.host=192.168.8.140
spring.redis.port=6379
spring.redis.database=0
spring.redis.password=
#spring.redis.
spring.redis.jedis.pool.max-active=8
spring.redis.jedis.pool.max-wait=-1
spring.redis.jedis.pool.max-idle=8
spring.redis.jedis.pool.min-idle=0
spring.redis.timeout=5000




15.2项目启动类

在这里插入图片描述

@SpringBootApplication(exclude= {SecurityAutoConfiguration.class})是为了暂时停用Spring Security认证
@EnableSwagger2 启用Swagger UI
@EnableCaching 启动缓存

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

//@Import(AuthenticationInterceptor.class)

@SpringBootApplication(exclude= {SecurityAutoConfiguration.class })
@EnableSwagger2
@EnableAspectJAutoProxy(proxyTargetClass=true)
@EnableCaching
public class DemoApplication {

	public static void main(String[] args) {
		SpringApplication.run(DemoApplication.class, args);
	}

}

16.数据库表结构

库:test
表结构:user

CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user` varchar(10) NOT NULL,
  `password` varchar(200) DEFAULT NULL,
  PRIMARY KEY (`id`,`user`),
  UNIQUE KEY `user_index` (`user`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=latin1;

表结构:sc

CREATE TABLE `sc` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `sid` varchar(10) DEFAULT NULL,
  `score` float(5,0) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=latin1;


17.1Swagger UI页面查看

在这里插入图片描述

17.2注册用户

此/register接口不需要token认证

在这里插入图片描述
在这里插入图片描述

18.获取token

此/login接口不需要token认证

在这里插入图片描述

在这里插入图片描述

19.请求需要校验token的API

POST请求添加成绩API

在这里插入图片描述

在这里插入图片描述

DELETE请求删除成绩API

在这里插入图片描述

20.分页显示GET请求结果

GET请求/scores接口

在这里插入图片描述

在这里插入图片描述

21.Redis缓存列表请求结果

Redis会将请求结果以每个API所属的"Controller名称::Controller名称#list(接口PATH[page,limit])"的格式为key存储在Redis中,当请求参数一致时,将直接从Redis返回数据,如下图
以下界面是Redis的官方客户端RedisInsight-v2

在这里插入图片描述

上图Redis就是缓存的下图

在这里插入图片描述

手动在数据库修改主键值为3的数据

在这里插入图片描述

再次GET请求/scores接口,数据没变化

在Redis中删除对应键值数据

在这里插入图片描述

再次GET请求/scores接口,结果如下,说明Redis缓存失效了,直接从数据库读取了数据。

在这里插入图片描述

  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值