什么是 rest-api-spring-boot-starter#
rest-api-spring-boot-starter 适用于SpringBoot Web API 快速构建让开发人员快速构建统一规范的业务RestFull API 不在去关心一些繁琐。重复工作,而是把重点聚焦到业务。
动机#
每次Web API常用功能都需要重新写一遍。或者复制之前的项目代码。于是我封装了这么一个stater
抽出SpringBoot Web API 每个项目必备需要重复写的模块,和必备功能。
并且扩展了我工作中用到的 所有工具库。 解放双手提高开发效率
推荐版本#
- SpringBoot
SpringBoot 2.7.xSpringBoot 2.7.xSpringBoot 2.7.x
版本更新#
目前最新版本1.6.2 支持功能如下:
-
支持一键配置自定义RestFull API 统一格式返回
-
支持RestFull API 错误国际化
-
支持全局异常处理,全局参数验证处理
-
业务错误断言工具封装,遵循错误优先返回原则
-
封装Redis key,value 操作工具类。统一key管理 spring cache缓存实现
-
RestTemplate 封装 POST,GET 请求工具
-
日志集成。自定义日志路径,按照日志等级分类,支持压缩和文件大小分割。按时间显示
-
工具库集成 集成了lombok,hutool,commons-lang3,guava。不需要自己单个引入
-
集成mybatisPlus一键代码生成
-
日志记录,服务监控,支持日志链路查询。自定义数据源
-
OpenApi3文档集成支持一键配置。支持多种文档和自动配置
-
生成JWT标准Token和权限认证
-
接口限流,Ip城市回显
-
HttpUserAgent请求设备工具封装
-
RequestUtil参数解析封装工具
Web JWT Token 权限支持#
JWT Web Token#
可以轻松自定义生成自己JWT Web Token
.和基于JWT 的userJwtToken
通过userJwtToken
你可以轻松生成基于用户登录认证的Token
@Autowiredprivate UserJwtToken userJwtToken;@GetMapping("/login")public Result login() {UserEntry userEntry = new UserEntry();userEntry.setUserId("2");userEntry.setUsername("billy");userEntry.setHobby("eat");userJwtToken.rememberMe=true;String token = userJwtToken.createdToken(userEntry.getUserId(), userEntry.getUsername(), userEntry);return Result.buildSuccess(token);}@Autowired private UserJwtToken userJwtToken; @GetMapping("/login") public Result login() { UserEntry userEntry = new UserEntry(); userEntry.setUserId("2"); userEntry.setUsername("billy"); userEntry.setHobby("eat"); userJwtToken.rememberMe=true; String token = userJwtToken.createdToken(userEntry.getUserId(), userEntry.getUsername(), userEntry); return Result.buildSuccess(token); }@Autowired private UserJwtToken userJwtToken; @GetMapping("/login") public Result login() { UserEntry userEntry = new UserEntry(); userEntry.setUserId("2"); userEntry.setUsername("billy"); userEntry.setHobby("eat"); userJwtToken.rememberMe=true; String token = userJwtToken.createdToken(userEntry.getUserId(), userEntry.getUsername(), userEntry); return Result.buildSuccess(token); }
解析token
获取用户信息
@GetMapping("/user")public Result getUser() {String token = "eyJhbGciOiJIUzI1NiIsInppcCI6IkRFRiJ9.eNqqViouTVKyUkrKzMmpVNJRyiwuBvKMgKyskkwoK7WiQMnK0MzC0tTUwsDEWEeptDi1SMmqGkx7pkBVgTh5ibmpSIZl5CclVQL5qYklSrW1AAAAAP__.8nWRs40LbRTIQBhJ8jVaANPcvsmX0zoLR66R-b2Uc4M";String userName=userJwtToken.getUserName(token);String userId= userJwtToken.getUserId(token);UserEntry userEntry=userJwtToken.parseUserToken(token,UserEntry.class);return Result.buildSuccess(userId);}@GetMapping("/user") public Result getUser() { String token = "eyJhbGciOiJIUzI1NiIsInppcCI6IkRFRiJ9.eNqqViouTVKyUkrKzMmpVNJRyiwuBvKMgKyskkwoK7WiQMnK0MzC0tTUwsDEWEeptDi1SMmqGkx7pkBVgTh5ibmpSIZl5CclVQL5qYklSrW1AAAAAP__.8nWRs40LbRTIQBhJ8jVaANPcvsmX0zoLR66R-b2Uc4M"; String userName=userJwtToken.getUserName(token); String userId= userJwtToken.getUserId(token); UserEntry userEntry=userJwtToken.parseUserToken(token,UserEntry.class); return Result.buildSuccess(userId); }@GetMapping("/user") public Result getUser() { String token = "eyJhbGciOiJIUzI1NiIsInppcCI6IkRFRiJ9.eNqqViouTVKyUkrKzMmpVNJRyiwuBvKMgKyskkwoK7WiQMnK0MzC0tTUwsDEWEeptDi1SMmqGkx7pkBVgTh5ibmpSIZl5CclVQL5qYklSrW1AAAAAP__.8nWRs40LbRTIQBhJ8jVaANPcvsmX0zoLR66R-b2Uc4M"; String userName=userJwtToken.getUserName(token); String userId= userJwtToken.getUserId(token); UserEntry userEntry=userJwtToken.parseUserToken(token,UserEntry.class); return Result.buildSuccess(userId); }
自定义Token秘钥和签名配置
jwt:secret: 123456 # 秘钥 建议加密后秘钥如md5 不要使用明文长度大于6位expiration: 86400 # token 过期时间(单位秒 1天后过期)token-header: Token #header token 名称remember-me-expiration: 604800 #记住我 token过期时间(单位秒 7天后过期)user-sign: true # 是否自定义签名。为true需要实现加密接口。和 配置 jwtCfg注入对应beanjwt: secret: 123456 # 秘钥 建议加密后秘钥如md5 不要使用明文长度大于6位 expiration: 86400 # token 过期时间(单位秒 1天后过期) token-header: Token #header token 名称 remember-me-expiration: 604800 #记住我 token过期时间(单位秒 7天后过期) user-sign: true # 是否自定义签名。为true需要实现加密接口。和 配置 jwtCfg注入对应beanjwt: secret: 123456 # 秘钥 建议加密后秘钥如md5 不要使用明文长度大于6位 expiration: 86400 # token 过期时间(单位秒 1天后过期) token-header: Token #header token 名称 remember-me-expiration: 604800 #记住我 token过期时间(单位秒 7天后过期) user-sign: true # 是否自定义签名。为true需要实现加密接口。和 配置 jwtCfg注入对应bean
自定义签名认证和动态秘钥授权需要实现UserSign
接口配置UserJwtConfig
配置类注入自定义签名bean
package cn.soboys.superaide.config;import cn.soboys.restapispringbootstarter.authorization.UserSign;import io.jsonwebtoken.SignatureAlgorithm;/*** @author 公众号 程序员三时* @version 1.0* @date 2023/7/16 00:20* @webSite https://github.com/coder-amiao*/public class MyUserSign implements UserSign {@Overridepublic SignatureAlgorithm sign() {return SignatureAlgorithm.HS256;}@Overridepublic String AuthKey() {return null;}}package cn.soboys.superaide.config; import cn.soboys.restapispringbootstarter.authorization.UserSign; import io.jsonwebtoken.SignatureAlgorithm; /** * @author 公众号 程序员三时 * @version 1.0 * @date 2023/7/16 00:20 * @webSite https://github.com/coder-amiao */ public class MyUserSign implements UserSign { @Override public SignatureAlgorithm sign() { return SignatureAlgorithm.HS256; } @Override public String AuthKey() { return null; } }package cn.soboys.superaide.config; import cn.soboys.restapispringbootstarter.authorization.UserSign; import io.jsonwebtoken.SignatureAlgorithm; /** * @author 公众号 程序员三时 * @version 1.0 * @date 2023/7/16 00:20 * @webSite https://github.com/coder-amiao */ public class MyUserSign implements UserSign { @Override public SignatureAlgorithm sign() { return SignatureAlgorithm.HS256; } @Override public String AuthKey() { return null; } }
AuthKey返回
null
时候会使用你在属性文件配置的秘钥。没有会使用默认的
package cn.soboys.superaide.config;import cn.soboys.restapispringbootstarter.authorization.*;import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.Primary;/*** @author 公众号 程序员三时* @version 1.0* @date 2023/7/15 09:49* @webSite https://github.com/coder-amiao* 用户jwt token生成配置*/@Configurationpublic class UserJwtConfig {@Beanpublic UserSign MyUserSign() {return new MyUserSign();}@Beanpublic UserJwtToken userJwtToken(UserSign MyUserSign) {UserJwtToken userJwtToken = new UserJwtToken();userJwtToken.setUserSign(MyUserSign);return userJwtToken;}}package cn.soboys.superaide.config; import cn.soboys.restapispringbootstarter.authorization.*; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; /** * @author 公众号 程序员三时 * @version 1.0 * @date 2023/7/15 09:49 * @webSite https://github.com/coder-amiao * 用户jwt token生成配置 */ @Configuration public class UserJwtConfig { @Bean public UserSign MyUserSign() { return new MyUserSign(); } @Bean public UserJwtToken userJwtToken(UserSign MyUserSign) { UserJwtToken userJwtToken = new UserJwtToken(); userJwtToken.setUserSign(MyUserSign); return userJwtToken; } }package cn.soboys.superaide.config; import cn.soboys.restapispringbootstarter.authorization.*; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; /** * @author 公众号 程序员三时 * @version 1.0 * @date 2023/7/15 09:49 * @webSite https://github.com/coder-amiao * 用户jwt token生成配置 */ @Configuration public class UserJwtConfig { @Bean public UserSign MyUserSign() { return new MyUserSign(); } @Bean public UserJwtToken userJwtToken(UserSign MyUserSign) { UserJwtToken userJwtToken = new UserJwtToken(); userJwtToken.setUserSign(MyUserSign); return userJwtToken; } }
权限认证#
基于JWT Web Token
也帮你封装了权限登录认证。 你只需要在属性文件配置开启即可。
jwt:authorization:has-authorization: trueincludes-url: /user # 需要认证请求 多个用逗号隔开excludes-url: /login,/register/** # 配置无需认证的jwt: authorization: has-authorization: true includes-url: /user # 需要认证请求 多个用逗号隔开 excludes-url: /login,/register/** # 配置无需认证的jwt: authorization: has-authorization: true includes-url: /user # 需要认证请求 多个用逗号隔开 excludes-url: /login,/register/** # 配置无需认证的
全局帮你自动处理Token
过期异常。和错误异常你只需要在heard中配置你自己的Token
就行
{"success": false,"code": "401","msg": "未授权 ","requestId": "9a3ytEtOX0UuojSaA2LD","timestamp": "2023-07-17 17:08:05","data": null}{ "success": false, "code": "401", "msg": "未授权 ", "requestId": "9a3ytEtOX0UuojSaA2LD", "timestamp": "2023-07-17 17:08:05", "data": null }{ "success": false, "code": "401", "msg": "未授权 ", "requestId": "9a3ytEtOX0UuojSaA2LD", "timestamp": "2023-07-17 17:08:05", "data": null }
如果需要自定义自己认证授权逻辑,实现LoginAuthorization
接口即可
并且在UserJwtConfig
配置类中注入对应LoginAuthorization
bean
如:
package cn.soboys.superaide.config;import cn.soboys.restapispringbootstarter.Assert;import cn.soboys.restapispringbootstarter.HttpStatus;import cn.soboys.restapispringbootstarter.authorization.LoginAuthorization;import cn.soboys.restapispringbootstarter.authorization.UserJwtToken;import org.dromara.hutool.core.text.StrUtil;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;/*** @author 公众号 程序员三时* @version 1.0* @date 2023/7/16 11:00* @webSite https://github.com/coder-amiao*/@Componentpublic class MyLoginAuthorization implements LoginAuthorization {@Autowiredprivate UserJwtToken userJwtToken;@Overridepublic Boolean authorization(HttpServletRequest request, HttpServletResponse response, Object handler) {String token = request.getHeader("Token");Assert.isFalse(StrUtil.isEmpty(token),HttpStatus.UNAUTHORIZED);String userId = userJwtToken.getUserId(token); //验证token有效合法性。//其他数据库 或者业务操作return true;}}package cn.soboys.superaide.config; import cn.soboys.restapispringbootstarter.Assert; import cn.soboys.restapispringbootstarter.HttpStatus; import cn.soboys.restapispringbootstarter.authorization.LoginAuthorization; import cn.soboys.restapispringbootstarter.authorization.UserJwtToken; import org.dromara.hutool.core.text.StrUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * @author 公众号 程序员三时 * @version 1.0 * @date 2023/7/16 11:00 * @webSite https://github.com/coder-amiao */ @Component public class MyLoginAuthorization implements LoginAuthorization { @Autowired private UserJwtToken userJwtToken; @Override public Boolean authorization(HttpServletRequest request, HttpServletResponse response, Object handler) { String token = request.getHeader("Token"); Assert.isFalse(StrUtil.isEmpty(token),HttpStatus.UNAUTHORIZED); String userId = userJwtToken.getUserId(token); //验证token有效合法性。 //其他数据库 或者业务操作 return true; } }package cn.soboys.superaide.config; import cn.soboys.restapispringbootstarter.Assert; import cn.soboys.restapispringbootstarter.HttpStatus; import cn.soboys.restapispringbootstarter.authorization.LoginAuthorization; import cn.soboys.restapispringbootstarter.authorization.UserJwtToken; import org.dromara.hutool.core.text.StrUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * @author 公众号 程序员三时 * @version 1.0 * @date 2023/7/16 11:00 * @webSite https://github.com/coder-amiao */ @Component public class MyLoginAuthorization implements LoginAuthorization { @Autowired private UserJwtToken userJwtToken; @Override public Boolean authorization(HttpServletRequest request, HttpServletResponse response, Object handler) { String token = request.getHeader("Token"); Assert.isFalse(StrUtil.isEmpty(token),HttpStatus.UNAUTHORIZED); String userId = userJwtToken.getUserId(token); //验证token有效合法性。 //其他数据库 或者业务操作 return true; } }
在配置类中注入bean
package cn.soboys.superaide.config;import cn.soboys.restapispringbootstarter.authorization.*;import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.Primary;/*** @author 公众号 程序员三时* @version 1.0* @date 2023/7/15 09:49* @webSite https://github.com/coder-amiao* 用户jwt token生成配置*/@Configurationpublic class UserJwtConfig {@Beanpublic UserSign MyUserSign() {return new MyUserSign();}@Bean@Primarypublic LoginAuthorization loginAuthorizationSubject() {return new MyLoginAuthorization();}@Beanpublic UserJwtToken userJwtToken(UserSign MyUserSign) {UserJwtToken userJwtToken = new UserJwtToken();userJwtToken.setUserSign(MyUserSign);return userJwtToken;}}package cn.soboys.superaide.config; import cn.soboys.restapispringbootstarter.authorization.*; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; /** * @author 公众号 程序员三时 * @version 1.0 * @date 2023/7/15 09:49 * @webSite https://github.com/coder-amiao * 用户jwt token生成配置 */ @Configuration public class UserJwtConfig { @Bean public UserSign MyUserSign() { return new MyUserSign(); } @Bean @Primary public LoginAuthorization loginAuthorizationSubject() { return new MyLoginAuthorization(); } @Bean public UserJwtToken userJwtToken(UserSign MyUserSign) { UserJwtToken userJwtToken = new UserJwtToken(); userJwtToken.setUserSign(MyUserSign); return userJwtToken; } }package cn.soboys.superaide.config; import cn.soboys.restapispringbootstarter.authorization.*; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; /** * @author 公众号 程序员三时 * @version 1.0 * @date 2023/7/15 09:49 * @webSite https://github.com/coder-amiao * 用户jwt token生成配置 */ @Configuration public class UserJwtConfig { @Bean public UserSign MyUserSign() { return new MyUserSign(); } @Bean @Primary public LoginAuthorization loginAuthorizationSubject() { return new MyLoginAuthorization(); } @Bean public UserJwtToken userJwtToken(UserSign MyUserSign) { UserJwtToken userJwtToken = new UserJwtToken(); userJwtToken.setUserSign(MyUserSign); return userJwtToken; } }
三方权限认证框架#
基于JWT Web Token也可以很轻松集成Shiro
或者是。Spring Security
等其他第三权限框架
当然后续版本我会把权限认证独立出来一个完整轻量级权限框架项目。如:
通过注解@hasPerm
,@hasRole
,@hasAnyPerm
,@hasAnyRoles
轻松实现相对复杂的权限认证。
后续更新#
通用业务#
在我们聚焦项目开发时候,总是会有一些相对公共独立的第三方业务模块。
如:三方登录
,三方支付
,消息推送
,资源上传
后续我会持续集成。通用业务生态。 实现真的解放生产力。自由组合。
有任何编程问题。
关注公众号,程序员三时 持续输出优质内容 希望给你带来一点启发和帮助