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

<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/> </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><!--8.0.11--></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></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绑定,实现增删改查 */@Mapperpublic 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绑定,实现增删改查 */@Mapperpublic 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,实现成绩模型增删改查 */@Servicepublic class ScoreService implements ScoreInterface {@Autowiredprivate ScoreMapper scoremapper;@Overridepublic int insert(ScoreDomain scoredomain) {return scoremapper.insert(scoredomain);}@Overridepublic int update(ScoreDomain scoredomain) {return scoremapper.update(scoredomain);}@Overridepublic int delete(String id) {return scoremapper.delete(Integer.valueOf(id));}@Overridepublic ScoreDomain get(String id) {return scoremapper.getScoreById(Integer.valueOf(id));}@Overridepublic 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实现用户模型查询 */@Servicepublic class UserService implements UserInterface {@Autowiredprivate UserMapper userMapper;/** * @param id 主键id * @return 用户实体 * 根据主键id查询用户实体 */@Overridepublic UserDomain getUserById(String id) {return userMapper.getUserById(Integer.valueOf(id));}/** * @param user 主键user * @return 用户实体 * 根据主键user查询用户实体 */@Overridepublic UserDomain getUserByUser(String user) {return userMapper.getUserByUser(user);}/** * @param userDomain 用户实体 * @return 序列化后的用户实体 */@Overridepublic 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 更新结果条数 */@Overridepublic int update(UserDomain userDomain) {return userMapper.update(userDomain);}@Overridepublic int delete(String id) {return userMapper.delete(Integer.valueOf(id));}/** * @param page页码 * @param limit 每页内容数量 * @return 用户实体列表 */@Overridepublic 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的前缀@RestControllerpublic class UserController {@AutowiredUserService userService;@AutowiredGetCheckToken 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的前缀@Componentpublic class ScoreController {@Autowiredprivate 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 {@Autowiredprivate HttpServletRequest httpServletRequest;@Overridepublic 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@ApiModelpublic 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 */@Componentpublic class AuthenticationInterceptor implements HandlerInterceptor {@Resourceprivate GetCheckToken getCheckToken;@Overridepublic 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;}@Overridepublic 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 {@Resourceprivate AuthenticationInterceptor authenticationInterceptor;@Overridepublic 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;@Configurationpublic 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@EnableSwagger2public class SwaggerConfig {/** * 给每个API设置必要请求头token,登录的API不应该设置,已在LoginController.java做了覆盖处理 */@BeanList<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;}@Beanpublic 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=rootspring.datasource.password=123456spring.datasource.url=jdbc:mysql://192.168.8.140:3306/test" />&useUnicode=true&characterEncoding=utf-8spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driverspring.mvc.pathmatch.matching-strategy=ANT_PATH_MATCHER#redis连接配置spring.redis.host=192.168.8.140spring.redis.port=6379spring.redis.database=0spring.redis.password=#spring.redis.spring.redis.jedis.pool.max-active=8spring.redis.jedis.pool.max-wait=-1spring.redis.jedis.pool.max-idle=8spring.redis.jedis.pool.min-idle=0spring.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)@EnableCachingpublic 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缓存失效了,直接从数据库读取了数据。