Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
C
cloud-backend
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
framework
cloud-backend
Commits
e67bc01a
Commit
e67bc01a
authored
May 30, 2022
by
袁伟铭
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
1.0.0
parent
4809b2b9
Hide whitespace changes
Inline
Side-by-side
Showing
23 changed files
with
639 additions
and
445 deletions
+639
-445
admin-server/src/main/java/com/zq/admin/config/security/SpringSecurityConfig.java
+0
-1
admin-server/src/main/java/com/zq/admin/config/security/TokenConfigurer.java
+0
-1
admin-server/src/main/java/com/zq/admin/config/security/TokenFilter.java
+0
-1
admin-server/src/main/java/com/zq/admin/config/security/TokenProvider.java
+117
-117
admin-server/src/main/java/com/zq/admin/modules/security/rest/AuthorizationController.java
+1
-1
api-server/src/main/java/com/zq/api/config/FeignConfig.java
+2
-1
api-server/src/main/java/com/zq/api/controller/ApiController.java
+12
-3
api-server/src/main/java/com/zq/api/controller/AppController.java
+145
-0
api-server/src/main/java/com/zq/api/form/ApiForm.java
+29
-6
api-server/src/main/java/com/zq/api/service/ApiService.java
+40
-11
api-server/src/main/java/com/zq/api/service/AppService.java
+91
-0
api-server/src/main/java/com/zq/api/service/impl/BaseApiLogic.java
+1
-1
api-server/src/main/java/com/zq/api/utils/ApiUtils.java
+20
-20
logging-server/src/main/java/com/zq/logging/constant/Constant.java
+2
-2
logging-server/src/main/java/com/zq/logging/utils/RequestUtils.java
+2
-2
user-server/src/main/java/com/zq/user/config/SpringSecurityConfig.java
+0
-1
user-server/src/main/java/com/zq/user/config/TokenConfigurer.java
+0
-1
user-server/src/main/java/com/zq/user/config/TokenFilter.java
+0
-1
user-server/src/main/java/com/zq/user/config/TokenProvider.java
+137
-137
xxx-common-utils/src/main/java/com/zq/common/config/base/UnifiedExceptionHandler.java
+6
-2
xxx-common-utils/src/main/java/com/zq/common/config/security/TokenProvider.java
+0
-136
xxx-common-utils/src/main/java/com/zq/common/constant/FeignHeader.java
+21
-0
xxx-common-utils/src/main/java/com/zq/common/constant/SystemName.java
+13
-0
No files found.
admin-server/src/main/java/com/zq/admin/config/security/SpringSecurityConfig.java
View file @
e67bc01a
...
...
@@ -19,7 +19,6 @@ import com.zq.admin.modules.security.service.OnlineUserService;
import
com.zq.admin.modules.security.service.UserCacheManager
;
import
com.zq.common.annotation.AnonymousAccess
;
import
com.zq.common.config.security.SecurityProperties
;
import
com.zq.common.config.security.TokenProvider
;
import
com.zq.common.utils.RequestMethodEnum
;
import
lombok.RequiredArgsConstructor
;
import
org.springframework.context.ApplicationContext
;
...
...
admin-server/src/main/java/com/zq/admin/config/security/TokenConfigurer.java
View file @
e67bc01a
...
...
@@ -18,7 +18,6 @@ package com.zq.admin.config.security;
import
com.zq.admin.modules.security.service.OnlineUserService
;
import
com.zq.admin.modules.security.service.UserCacheManager
;
import
com.zq.common.config.security.SecurityProperties
;
import
com.zq.common.config.security.TokenProvider
;
import
lombok.RequiredArgsConstructor
;
import
org.springframework.security.config.annotation.SecurityConfigurerAdapter
;
import
org.springframework.security.config.annotation.web.builders.HttpSecurity
;
...
...
admin-server/src/main/java/com/zq/admin/config/security/TokenFilter.java
View file @
e67bc01a
...
...
@@ -19,7 +19,6 @@ import cn.hutool.core.util.StrUtil;
import
com.zq.admin.modules.security.service.OnlineUserService
;
import
com.zq.admin.modules.security.service.UserCacheManager
;
import
com.zq.common.config.security.SecurityProperties
;
import
com.zq.common.config.security.TokenProvider
;
import
com.zq.common.context.ContextUtils
;
import
com.zq.common.vo.OnlineUserDto
;
import
io.jsonwebtoken.ExpiredJwtException
;
...
...
admin-server/src/main/java/com/zq/admin/config/security/TokenProvider.java
View file @
e67bc01a
//
package com.zq.admin.config.security;/*
//
* Copyright 2019-2020 Zheng Jie
//
*
//
* Licensed under the Apache License, Version 2.0 (the "License");
//
* you may not use this file except in compliance with the License.
//
* You may obtain a copy of the License at
//
*
//
* http://www.apache.org/licenses/LICENSE-2.0
//
*
//
* Unless required by applicable law or agreed to in writing, software
//
* distributed under the License is distributed on an "AS IS" BASIS,
//
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
* See the License for the specific language governing permissions and
//
* limitations under the License.
//
*/
//
//
import cn.hutool.core.date.DateField;
//
import cn.hutool.core.date.DateUtil;
//
import cn.hutool.core.util.IdUtil;
//
import com.zq.common.config.redis.RedisUtils;
//
import com.zq.common.config.security.SecurityProperties;
//
import io.jsonwebtoken.Claims;
//
import io.jsonwebtoken.Jwts;
//
import io.jsonwebtoken.SignatureAlgorithm;
//
import lombok.RequiredArgsConstructor;
//
import lombok.extern.slf4j.Slf4j;
//
import org.apache.commons.lang3.StringUtils;
//
import org.springframework.beans.factory.InitializingBean;
//
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
//
import org.springframework.security.core.Authentication;
//
import org.springframework.security.core.userdetails.User;
//
import org.springframework.stereotype.Component;
//
//
import javax.crypto.spec.SecretKeySpec;
//
import javax.servlet.http.HttpServletRequest;
//
import javax.xml.bind.DatatypeConverter;
//
import java.security.Key;
//
import java.util.ArrayList;
//
import java.util.Date;
//
import java.util.concurrent.TimeUnit;
//
/
/ /
**
//
* @author /
//
*/
//
@Slf4j
//
@Component
//
@RequiredArgsConstructor
//
public class TokenProvider implements InitializingBean {
//
//
private final RedisUtils redisUtils;
//
private final SecurityProperties properties;
//
//
public static final String AUTHORITIES_KEY = "auth";
//
private static Key key;
//
private static SignatureAlgorithm signatureAlgorithm;
//
//
@Override
//
public void afterPropertiesSet() {
//
signatureAlgorithm = SignatureAlgorithm.HS512;
//
byte[] keyBytes = DatatypeConverter.parseBase64Binary(properties.getBase64Secret());
//
key = new SecretKeySpec(keyBytes, signatureAlgorithm.getJcaName());
//
}
//
//
public static String createToken(Authentication authentication) {
//
return Jwts.builder()
//
.claim(AUTHORITIES_KEY, authentication.getName())
//
.setSubject(authentication.getName())
//
.signWith(signatureAlgorithm, key)
//
// 加入ID确保生成的 Token 都不一致
//
.setId(IdUtil.simpleUUID())
//
.compact();
//
}
//
//
public Claims getClaims(String token) {
//
return Jwts.parser()
//
.setSigningKey(DatatypeConverter.parseBase64Binary(properties.getBase64Secret()))
//
.parseClaimsJws(token)
//
.getBody();
//
}
//
//
public Authentication getAuthentication(String token) {
//
Claims claims = getClaims(token);
//
User principal = new User(claims.getSubject(), "******", new ArrayList<>());
//
return new UsernamePasswordAuthenticationToken(principal, token, new ArrayList<>());
//
}
//
//
/**
//
* @param token 需要检查的token
//
*/
//
public void checkRenewal(String token) {
//
// 判断是否续期token,计算token的过期时间
//
long time = redisUtils.getExpire(properties.getOnlineKey() + token) * 1000;
//
Date expireDate = DateUtil.offset(new Date(), DateField.MILLISECOND, (int) time);
//
// 判断当前时间与过期时间的时间差
//
long differ = expireDate.getTime() - System.currentTimeMillis();
//
// 如果在续期检查的范围内,则续期
//
if (differ <= properties.getDetect()) {
//
long renew = time + properties.getRenew();
//
redisUtils.expire(properties.getOnlineKey() + token, renew, TimeUnit.MILLISECONDS);
//
}
//
}
//
//
public String getToken(HttpServletRequest request) {
//
String bearerToken = request.getHeader(properties.getHeader());
//
if (StringUtils.isBlank(bearerToken)) {
//
return null;
//
}
//
if (bearerToken.startsWith(properties.getTokenStartWith())) {
//
// 去掉令牌前缀
//
return bearerToken.replace(properties.getTokenStartWith(), "");
//
} else {
//
log.debug("非法Token:{}", bearerToken);
//
}
//
return null;
//
}
//
//
}
package
com
.
zq
.
admin
.
config
.
security
;
/*
* Copyright 2019-2020 Zheng Jie
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import
cn.hutool.core.date.DateField
;
import
cn.hutool.core.date.DateUtil
;
import
cn.hutool.core.util.IdUtil
;
import
com.zq.common.config.redis.RedisUtils
;
import
com.zq.common.config.security.SecurityProperties
;
import
io.jsonwebtoken.Claims
;
import
io.jsonwebtoken.Jwts
;
import
io.jsonwebtoken.SignatureAlgorithm
;
import
lombok.RequiredArgsConstructor
;
import
lombok.extern.slf4j.Slf4j
;
import
org.apache.commons.lang3.StringUtils
;
import
org.springframework.beans.factory.InitializingBean
;
import
org.springframework.security.authentication.UsernamePasswordAuthenticationToken
;
import
org.springframework.security.core.Authentication
;
import
org.springframework.security.core.userdetails.User
;
import
org.springframework.stereotype.Component
;
import
javax.crypto.spec.SecretKeySpec
;
import
javax.servlet.http.HttpServletRequest
;
import
javax.xml.bind.DatatypeConverter
;
import
java.security.Key
;
import
java.util.ArrayList
;
import
java.util.Date
;
import
java.util.concurrent.TimeUnit
;
/**
* @author /
*/
@Slf4j
@Component
@RequiredArgsConstructor
public
class
TokenProvider
implements
InitializingBean
{
private
final
RedisUtils
redisUtils
;
private
final
SecurityProperties
properties
;
public
static
final
String
AUTHORITIES_KEY
=
"auth"
;
private
static
Key
key
;
private
static
SignatureAlgorithm
signatureAlgorithm
;
@Override
public
void
afterPropertiesSet
()
{
signatureAlgorithm
=
SignatureAlgorithm
.
HS512
;
byte
[]
keyBytes
=
DatatypeConverter
.
parseBase64Binary
(
properties
.
getBase64Secret
());
key
=
new
SecretKeySpec
(
keyBytes
,
signatureAlgorithm
.
getJcaName
());
}
public
static
String
createToken
(
Authentication
authentication
)
{
return
Jwts
.
builder
()
.
claim
(
AUTHORITIES_KEY
,
authentication
.
getName
())
.
setSubject
(
authentication
.
getName
())
.
signWith
(
signatureAlgorithm
,
key
)
// 加入ID确保生成的 Token 都不一致
.
setId
(
IdUtil
.
simpleUUID
())
.
compact
();
}
public
Claims
getClaims
(
String
token
)
{
return
Jwts
.
parser
()
.
setSigningKey
(
DatatypeConverter
.
parseBase64Binary
(
properties
.
getBase64Secret
()))
.
parseClaimsJws
(
token
)
.
getBody
();
}
public
Authentication
getAuthentication
(
String
token
)
{
Claims
claims
=
getClaims
(
token
);
User
principal
=
new
User
(
claims
.
getSubject
(),
"******"
,
new
ArrayList
<>());
return
new
UsernamePasswordAuthenticationToken
(
principal
,
token
,
new
ArrayList
<>());
}
/**
* @param token 需要检查的token
*/
public
void
checkRenewal
(
String
token
)
{
// 判断是否续期token,计算token的过期时间
long
time
=
redisUtils
.
getExpire
(
properties
.
getOnlineKey
()
+
token
)
*
1000
;
Date
expireDate
=
DateUtil
.
offset
(
new
Date
(),
DateField
.
MILLISECOND
,
(
int
)
time
);
// 判断当前时间与过期时间的时间差
long
differ
=
expireDate
.
getTime
()
-
System
.
currentTimeMillis
();
// 如果在续期检查的范围内,则续期
if
(
differ
<=
properties
.
getDetect
())
{
long
renew
=
time
+
properties
.
getRenew
();
redisUtils
.
expire
(
properties
.
getOnlineKey
()
+
token
,
renew
,
TimeUnit
.
MILLISECONDS
);
}
}
public
String
getToken
(
HttpServletRequest
request
)
{
String
bearerToken
=
request
.
getHeader
(
properties
.
getHeader
());
if
(
StringUtils
.
isBlank
(
bearerToken
))
{
return
null
;
}
if
(
bearerToken
.
startsWith
(
properties
.
getTokenStartWith
()))
{
// 去掉令牌前缀
return
bearerToken
.
replace
(
properties
.
getTokenStartWith
(),
""
);
}
else
{
log
.
debug
(
"非法Token:{}"
,
bearerToken
);
}
return
null
;
}
}
admin-server/src/main/java/com/zq/admin/modules/security/rest/AuthorizationController.java
View file @
e67bc01a
...
...
@@ -20,6 +20,7 @@ import com.wf.captcha.base.Captcha;
import
com.zq.admin.config.RsaProperties
;
import
com.zq.admin.config.bean.LoginCodeEnum
;
import
com.zq.admin.config.bean.LoginProperties
;
import
com.zq.admin.config.security.TokenProvider
;
import
com.zq.admin.exception.BadRequestException
;
import
com.zq.admin.modules.security.service.OnlineUserService
;
import
com.zq.admin.modules.security.service.dto.AuthUserDto
;
...
...
@@ -31,7 +32,6 @@ import com.zq.common.annotation.rest.AnonymousGetMapping;
import
com.zq.common.annotation.rest.AnonymousPostMapping
;
import
com.zq.common.config.redis.RedisUtils
;
import
com.zq.common.config.security.SecurityProperties
;
import
com.zq.common.config.security.TokenProvider
;
import
io.swagger.annotations.Api
;
import
io.swagger.annotations.ApiOperation
;
import
lombok.RequiredArgsConstructor
;
...
...
api-server/src/main/java/com/zq/api/config/FeignConfig.java
View file @
e67bc01a
package
com
.
zq
.
api
.
config
;
import
com.zq.common.constant.FeignHeader
;
import
feign.RequestInterceptor
;
import
feign.RequestTemplate
;
import
org.springframework.context.annotation.Bean
;
...
...
@@ -55,7 +56,7 @@ public class FeignConfig {
@Override
public
void
apply
(
RequestTemplate
template
)
{
HttpServletRequest
request
=
((
ServletRequestAttributes
)
Objects
.
requireNonNull
(
RequestContextHolder
.
getRequestAttributes
())).
getRequest
();
template
.
header
(
"X-App-Token"
,
request
.
getParameter
(
"token"
));
template
.
header
(
FeignHeader
.
API_TOKEN
,
request
.
getParameter
(
"token"
));
Enumeration
<
String
>
headerNames
=
request
.
getHeaderNames
();
if
(
headerNames
!=
null
)
{
while
(
headerNames
.
hasMoreElements
())
{
...
...
api-server/src/main/java/com/zq/api/controller/ApiController.java
View file @
e67bc01a
...
...
@@ -17,6 +17,7 @@ import feign.FeignException;
import
io.swagger.annotations.Api
;
import
lombok.RequiredArgsConstructor
;
import
lombok.extern.slf4j.Slf4j
;
import
org.springframework.web.bind.annotation.RequestHeader
;
import
org.springframework.web.bind.annotation.RequestMapping
;
import
org.springframework.web.bind.annotation.RestController
;
...
...
@@ -43,7 +44,7 @@ public class ApiController {
* 2016年10月3日 下午1:38:27
*/
@RequestMapping
(
"/action"
)
public
ApiResp
action
(
HttpServletRequest
request
,
ApiForm
form
)
{
public
ApiResp
action
(
HttpServletRequest
request
,
ApiForm
form
,
@RequestHeader
(
required
=
false
)
String
token
)
{
long
start
=
System
.
currentTimeMillis
();
// 不处理Request Method:OPTIONS的请求
...
...
@@ -51,6 +52,7 @@ public class ApiController {
return
ApiUtils
.
getSuccessResp
(
form
);
}
form
.
setType
(
2
);
//解析业务参数
if
(!
form
.
parseBizContent
())
{
return
ApiUtils
.
getParamError
(
form
);
...
...
@@ -84,7 +86,12 @@ public class ApiController {
// 调用接口方法
ApiResp
resp
;
try
{
resp
=
apiService
.
action
(
form
);
// 身份验证
resp
=
apiService
.
auth
(
form
,
token
);
if
(
resp
.
isSuccess
())
{
// 调用接口方法
resp
=
apiService
.
action
(
form
);
}
}
catch
(
Exception
e
)
{
log
.
error
(
"调用方法异常:{}"
,
e
.
getMessage
());
stackTrace
=
ThrowableUtil
.
getStackTrace
(
e
);
...
...
@@ -95,6 +102,8 @@ public class ApiController {
resp
=
ApiUtils
.
toApiResp
(
form
,
ResultVo
.
fail
(
404
,
"NotFound"
));
}
else
if
(
stackTrace
.
contains
(
"Load balancer does not have available server for client"
))
{
resp
=
ApiUtils
.
getServiceNotAvailableError
(
form
);
}
else
if
(
stackTrace
.
contains
(
"Connection refused: connect executing"
))
{
resp
=
ApiUtils
.
getServiceNotAvailableError
(
form
);
}
else
{
resp
=
ApiUtils
.
getMethodHandlerError
(
form
);
}
...
...
@@ -102,7 +111,7 @@ public class ApiController {
// 没有数据输出空
resp
=
resp
==
null
?
new
ApiResp
(
form
)
:
resp
;
String
logType
=
resp
.
isSuccess
()
?
"INFO"
:
"ERROR"
;
String
logType
=
resp
.
isSuccess
()
?
"INFO"
:
"
400"
.
equals
(
resp
.
getCode
())
?
"WARN"
:
"
ERROR"
;
// 如果是500错误, 服务会返回错误的堆栈信息
if
(
resp
.
getCode
().
equals
(
ApiCodeEnum
.
SERVER_ERROR
.
code
()))
{
...
...
api-server/src/main/java/com/zq/api/controller/AppController.java
0 → 100644
View file @
e67bc01a
package
com
.
zq
.
api
.
controller
;
import
cn.hutool.core.exceptions.ExceptionUtil
;
import
cn.hutool.core.util.StrUtil
;
import
cn.hutool.extra.servlet.ServletUtil
;
import
com.alibaba.fastjson.JSON
;
import
com.zq.api.constant.ApiCodeEnum
;
import
com.zq.api.form.ApiForm
;
import
com.zq.api.form.ApiResp
;
import
com.zq.api.service.AppService
;
import
com.zq.api.utils.ApiUtils
;
import
com.zq.common.config.security.ApiTokenUtils
;
import
com.zq.common.utils.ThrowableUtil
;
import
com.zq.common.vo.ApiTokenVo
;
import
com.zq.common.vo.ResultVo
;
import
feign.FeignException
;
import
io.swagger.annotations.Api
;
import
lombok.RequiredArgsConstructor
;
import
lombok.extern.slf4j.Slf4j
;
import
org.springframework.web.bind.annotation.RequestMapping
;
import
org.springframework.web.bind.annotation.RestController
;
import
javax.servlet.http.HttpServletRequest
;
import
java.util.Arrays
;
/**
* @author wilmiam
* @since 2021-08-16 15:37
*/
@Api
(
tags
=
"APP接口"
)
@Slf4j
@RestController
@RequiredArgsConstructor
@RequestMapping
(
"/api/app"
)
public
class
AppController
{
private
final
AppService
appService
;
/**
* 允许用户未登录状态下执行的方法名
*/
private
final
String
[]
allowMethod
=
{
"apiLogin"
,
"signinWxLogin"
,
"getSigninWxPhone"
,
"wxLogin"
,
"getWxPhone"
};
/**
* 获取信息入口
* <p>
* 2016年10月3日 下午1:38:27
*/
@RequestMapping
(
"/action"
)
public
ApiResp
action
(
HttpServletRequest
request
,
ApiForm
form
)
{
long
start
=
System
.
currentTimeMillis
();
// 不处理Request Method:OPTIONS的请求
if
(
"OPTIONS"
.
equals
(
request
.
getMethod
()))
{
return
ApiUtils
.
getSuccessResp
(
form
);
}
form
.
setType
(
1
);
//解析业务参数
if
(!
form
.
parseBizContent
())
{
return
ApiUtils
.
getParamError
(
form
);
}
String
method
=
form
.
getMethod
();
if
(
StrUtil
.
isBlank
(
method
))
{
method
=
request
.
getParameter
(
"method"
);
form
.
setMethod
(
method
);
}
if
(
StrUtil
.
isBlank
(
form
.
getToken
()))
{
boolean
contains
=
Arrays
.
asList
(
allowMethod
).
contains
(
method
);
if
(!
contains
)
{
return
ApiUtils
.
getLoginValidError
(
form
);
}
}
ApiTokenVo
tokenVo
=
ApiTokenUtils
.
getAppTokenVo
(
form
.
getToken
());
if
(
tokenVo
==
null
)
{
boolean
contains
=
Arrays
.
asList
(
allowMethod
).
contains
(
method
);
if
(!
contains
)
{
return
ApiUtils
.
getLoginValidError
(
form
);
}
}
else
{
form
.
setUserId
(
String
.
valueOf
(
tokenVo
.
getUserId
()));
form
.
setApiTokenVo
(
tokenVo
);
}
String
stackTrace
=
""
;
// 调用接口方法
ApiResp
resp
;
try
{
resp
=
appService
.
action
(
form
);
}
catch
(
Exception
e
)
{
log
.
error
(
"调用方法异常:{}"
,
e
.
getMessage
());
stackTrace
=
ThrowableUtil
.
getStackTrace
(
e
);
// 判断指定异常是否来自或者包含指定异常
if
(
ExceptionUtil
.
isFromOrSuppressedThrowable
(
e
,
FeignException
.
Unauthorized
.
class
))
{
resp
=
ApiUtils
.
toApiResp
(
form
,
ResultVo
.
fail
(
401
,
"Unauthorized"
));
}
else
if
(
ExceptionUtil
.
isFromOrSuppressedThrowable
(
e
,
FeignException
.
NotFound
.
class
))
{
resp
=
ApiUtils
.
toApiResp
(
form
,
ResultVo
.
fail
(
404
,
"NotFound"
));
}
else
if
(
stackTrace
.
contains
(
"Load balancer does not have available server for client"
))
{
resp
=
ApiUtils
.
getServiceNotAvailableError
(
form
);
}
else
if
(
stackTrace
.
contains
(
"Connection refused: connect executing"
))
{
resp
=
ApiUtils
.
getServiceNotAvailableError
(
form
);
}
else
{
resp
=
ApiUtils
.
getMethodHandlerError
(
form
);
}
}
// 没有数据输出空
resp
=
resp
==
null
?
new
ApiResp
(
form
)
:
resp
;
String
logType
=
resp
.
isSuccess
()
?
"INFO"
:
"400"
.
equals
(
resp
.
getCode
())
?
"WARN"
:
"ERROR"
;
// 如果是500错误, 服务会返回错误的堆栈信息
if
(
resp
.
getCode
().
equals
(
ApiCodeEnum
.
SERVER_ERROR
.
code
()))
{
stackTrace
=
resp
.
getMsg
();
resp
.
setMsg
(
ApiCodeEnum
.
SERVER_ERROR
.
msg
());
}
// 调试日志
if
(
ApiUtils
.
DEBUG
)
{
System
.
out
.
println
(
"API DEBUG ACTION \n[from="
+
form
+
"]"
+
"\n[resp="
+
JSON
.
toJSONString
(
resp
)
+
"]"
+
"\n[time="
+
(
System
.
currentTimeMillis
()
-
start
)
+
"ms]"
);
}
String
clientIp
=
ServletUtil
.
getClientIP
(
request
);
appService
.
addLog
(
form
,
clientIp
,
logType
,
resp
.
getMsg
(),
stackTrace
,
System
.
currentTimeMillis
()
-
start
);
return
resp
;
}
/**
* 开关调试日志
* <p>
* 2016年10月3日 下午5:47:46
*/
@RequestMapping
(
"/debug"
)
public
ApiResp
debug
(
HttpServletRequest
request
)
{
ApiForm
from
=
ServletUtil
.
toBean
(
request
,
ApiForm
.
class
,
true
);
ApiUtils
.
DEBUG
=
!
ApiUtils
.
DEBUG
;
return
new
ApiResp
(
from
).
setData
(
ApiUtils
.
DEBUG
);
}
}
api-server/src/main/java/com/zq/api/form/ApiForm.java
View file @
e67bc01a
...
...
@@ -37,6 +37,8 @@ public class ApiForm {
private
String
nonce
;
// 随机字串(建议使用UUID)
private
String
version
;
// 接口版本
private
String
apiNo
;
// 接口码
private
Integer
type
;
// 1-app;2-api,内部定
private
String
clientType
;
// 客户端类型 xcx-小程序,H5,api
private
String
bizContent
;
// 请求业务参数
private
JSONObject
bizContentJson
;
// 请求业务的json对象
private
MultipartFile
file
;
// 上传文件用
...
...
@@ -44,12 +46,19 @@ public class ApiForm {
public
boolean
parseBizContent
()
{
try
{
// API参数是否加密
boolean
flag
=
ConfigCache
.
getValueToBoolean
(
"API.PARAM.ENCRYPT"
);
if
(
StrUtil
.
isNotBlank
(
bizContent
)
&&
flag
)
{
bizContent
=
ApiUtils
.
decode
(
bizContent
);
if
(
type
==
1
)
{
// API参数是否加密
boolean
flag
=
ConfigCache
.
getValueToBoolean
(
"API.PARAM.ENCRYPT"
);
if
(
StrUtil
.
isNotBlank
(
bizContent
)
&&
flag
)
{
bizContent
=
ApiUtils
.
decode
(
bizContent
,
""
);
}
}
else
{
if
(
StrUtil
.
isNotBlank
(
bizContent
))
{
bizContent
=
ApiUtils
.
decode
(
bizContent
,
"BASE64"
);
}
}
if
(
StrUtil
.
isBlank
(
bizContent
))
{
if
(
bizContent
==
null
)
{
bizContent
=
""
;
}
bizContentJson
=
JSON
.
parseObject
(
bizContent
);
...
...
@@ -148,8 +157,9 @@ public class ApiForm {
public
TreeMap
<
String
,
String
>
getSignTreeMap
()
{
TreeMap
<
String
,
String
>
treeMap
=
new
TreeMap
<>();
treeMap
.
put
(
"appId"
,
this
.
appId
);
treeMap
.
put
(
"apiNo"
,
this
.
apiNo
);
treeMap
.
put
(
"timestamp"
,
this
.
timestamp
);
treeMap
.
put
(
"nonce"
,
this
.
nonce
);
treeMap
.
put
(
"method"
,
this
.
method
);
treeMap
.
put
(
"version"
,
this
.
version
);
String
bizContent
=
StrUtil
.
isBlank
(
this
.
bizContent
)
?
""
:
this
.
bizContent
;
...
...
@@ -157,4 +167,17 @@ public class ApiForm {
return
treeMap
;
}
public
String
getSignStr
(
String
key
)
{
TreeMap
<
String
,
String
>
treeMap
=
getSignTreeMap
();
// 原始请求串
StringBuilder
src
=
new
StringBuilder
();
for
(
Map
.
Entry
<
String
,
String
>
entry
:
treeMap
.
entrySet
())
{
src
.
append
(
entry
.
getKey
()).
append
(
"="
).
append
(
entry
.
getValue
()).
append
(
"&"
);
}
src
.
append
(
"key="
).
append
(
key
);
return
src
.
toString
();
}
}
api-server/src/main/java/com/zq/api/service/ApiService.java
View file @
e67bc01a
package
com
.
zq
.
api
.
service
;
import
cn.hutool.core.date.DateUtil
;
import
com.zq.api.config.ConfigCache
;
import
com.zq.api.constant.ApiCodeEnum
;
import
cn.hutool.core.util.StrUtil
;
import
com.zq.api.dao.ApiLogDao
;
import
com.zq.api.entity.ApiLog
;
import
com.zq.api.form.ApiForm
;
import
com.zq.api.form.ApiResp
;
import
com.zq.api.utils.ApiUtils
;
import
com.zq.api.utils.ReflectionUtils
;
import
com.zq.common.config.redis.BaseCacheKeys
;
import
com.zq.common.config.redis.RedisUtils
;
import
com.zq.common.vo.ApiTokenVo
;
import
lombok.RequiredArgsConstructor
;
import
lombok.extern.slf4j.Slf4j
;
import
org.springframework.scheduling.annotation.Async
;
...
...
@@ -16,6 +18,7 @@ import org.springframework.stereotype.Service;
import
java.lang.reflect.Method
;
import
java.util.ArrayList
;
import
java.util.Arrays
;
import
java.util.List
;
@Slf4j
...
...
@@ -24,6 +27,10 @@ import java.util.List;
public
class
ApiService
{
private
final
ApiLogDao
apiLogDao
;
private
final
RedisUtils
redisUtils
;
// 允许用户未登录状态下执行的方法名
private
final
String
[]
allowMethod
=
{
"getApiToken"
};
private
static
final
List
<
String
>
METHOD_LIST
;
...
...
@@ -48,20 +55,42 @@ public class ApiService {
return
methodList
;
}
/**
* 身份验证
*
* @param form
* @return
*/
public
ApiResp
auth
(
ApiForm
form
,
String
token
)
{
boolean
contains
=
Arrays
.
asList
(
allowMethod
).
contains
(
form
.
getMethod
());
if
(
contains
)
{
return
ApiUtils
.
getSuccessResp
(
form
);
}
else
if
(
StrUtil
.
isBlank
(
token
))
{
return
ApiUtils
.
getLoginValidError
(
form
);
}
// 验证认证信息
ApiTokenVo
tokenVo
=
redisUtils
.
getObj
(
BaseCacheKeys
.
PREFIX
+
"ApiToken."
+
token
,
ApiTokenVo
.
class
);
if
(
tokenVo
==
null
)
{
return
ApiUtils
.
getLoginValidError
(
form
);
}
// 验证签名
String
sign
=
ApiUtils
.
getSign
(
form
.
getSignStr
(
tokenVo
.
getSessionKey
()
==
null
?
""
:
tokenVo
.
getSessionKey
()));
if
(!
sign
.
equals
(
form
.
getSign
()))
{
return
ApiUtils
.
getCheckSignValidError
(
form
);
}
form
.
setUserId
(
tokenVo
.
getUserId
());
form
.
setApiTokenVo
(
tokenVo
);
return
ApiUtils
.
getSuccessResp
(
form
);
}
public
ApiResp
action
(
ApiForm
form
)
throws
Exception
{
if
(!
METHOD_LIST
.
contains
(
form
.
getMethod
()))
{
return
ApiUtils
.
getMethodError
(
form
);
}
// 签名验证标识
boolean
validFlag
=
ConfigCache
.
getValueToBoolean
(
"API.SIGN.VALID"
);
IApiLogic
apiLogic
=
getApiLogic
(
form
);
if
(
validFlag
)
{
// 验证签名
ApiResp
validResp
=
apiLogic
.
signValid
(
form
);
if
(!
validResp
.
getCode
().
equals
(
ApiCodeEnum
.
SUCCESS
.
code
()))
{
return
validResp
;
}
}
// 调用接口方法,利用反射更简洁
return
(
ApiResp
)
ReflectionUtils
.
invokeMethod
(
apiLogic
,
form
.
getMethod
(),
new
Class
<?>[]{
ApiForm
.
class
},
new
Object
[]{
form
});
...
...
api-server/src/main/java/com/zq/api/service/AppService.java
0 → 100644
View file @
e67bc01a
package
com
.
zq
.
api
.
service
;
import
cn.hutool.core.date.DateUtil
;
import
com.zq.api.config.ConfigCache
;
import
com.zq.api.constant.ApiCodeEnum
;
import
com.zq.api.dao.ApiLogDao
;
import
com.zq.api.entity.ApiLog
;
import
com.zq.api.form.ApiForm
;
import
com.zq.api.form.ApiResp
;
import
com.zq.api.utils.ApiUtils
;
import
com.zq.api.utils.ReflectionUtils
;
import
lombok.RequiredArgsConstructor
;
import
lombok.extern.slf4j.Slf4j
;
import
org.springframework.scheduling.annotation.Async
;
import
org.springframework.stereotype.Service
;
import
java.lang.reflect.Method
;
import
java.util.ArrayList
;
import
java.util.List
;
/**
* @author wilmiam
* @since 2022/5/30 12:33
*/
@Slf4j
@Service
@RequiredArgsConstructor
public
class
AppService
{
private
final
ApiLogDao
apiLogDao
;
private
static
final
List
<
String
>
METHOD_LIST
;
static
{
METHOD_LIST
=
methodList
();
}
public
IApiLogic
getApiLogic
(
ApiForm
form
)
{
IApiLogic
apiLogic
=
ApiUtils
.
getApiLogic
(
form
);
return
apiLogic
;
}
public
static
List
<
String
>
methodList
()
{
List
<
String
>
methodList
=
new
ArrayList
<>();
Method
[]
methods
=
IApiLogic
.
class
.
getMethods
();
for
(
Method
method
:
methods
)
{
Class
<?>[]
params
=
method
.
getParameterTypes
();
if
(
params
.
length
==
1
&&
(
params
[
0
]
==
ApiForm
.
class
))
{
methodList
.
add
(
method
.
getName
());
}
}
return
methodList
;
}
public
ApiResp
action
(
ApiForm
form
)
throws
Exception
{
if
(!
METHOD_LIST
.
contains
(
form
.
getMethod
()))
{
return
ApiUtils
.
getMethodError
(
form
);
}
// 签名验证标识
boolean
validFlag
=
ConfigCache
.
getValueToBoolean
(
"API.SIGN.VALID"
);
IApiLogic
apiLogic
=
getApiLogic
(
form
);
if
(
validFlag
)
{
// 验证签名
ApiResp
validResp
=
apiLogic
.
signValid
(
form
);
if
(!
validResp
.
getCode
().
equals
(
ApiCodeEnum
.
SUCCESS
.
code
()))
{
return
validResp
;
}
}
// 调用接口方法,利用反射更简洁
return
(
ApiResp
)
ReflectionUtils
.
invokeMethod
(
apiLogic
,
form
.
getMethod
(),
new
Class
<?>[]{
ApiForm
.
class
},
new
Object
[]{
form
});
}
@Async
public
void
addLog
(
ApiForm
form
,
String
ip
,
String
logType
,
String
respMsg
,
String
errorInfo
,
Long
timeCost
)
{
apiLogDao
.
insert
(
ApiLog
.
builder
()
.
appId
(
form
.
getAppId
())
.
userId
(
form
.
getUserId
())
.
method
(
form
.
getMethod
())
.
version
(
form
.
getVersion
())
.
bizContent
(
form
.
getBizContent
())
.
ip
(
ip
)
.
logType
(
logType
)
.
respMsg
(
respMsg
)
.
stackTrace
(
errorInfo
)
.
timeCost
(
timeCost
)
.
createTime
(
DateUtil
.
date
().
toJdkDate
())
.
build
());
}
}
api-server/src/main/java/com/zq/api/service/impl/BaseApiLogic.java
View file @
e67bc01a
...
...
@@ -27,7 +27,7 @@ public abstract class BaseApiLogic implements IApiLogic {
return
ApiUtils
.
getCheckSignValidError
(
form
);
}
String
serverSign
=
ApiUtils
.
getSign
(
form
.
getSign
TreeMap
(
));
String
serverSign
=
ApiUtils
.
getSign
(
form
.
getSign
Str
(
""
));
if
(!
serverSign
.
equals
(
form
.
getSign
()))
{
return
ApiUtils
.
getCheckSignValidError
(
form
);
}
...
...
api-server/src/main/java/com/zq/api/utils/ApiUtils.java
View file @
e67bc01a
...
...
@@ -13,12 +13,8 @@ import com.zq.common.vo.ResultVo;
import
org.apache.commons.lang3.StringUtils
;
import
org.springframework.stereotype.Component
;
import
java.io.UnsupportedEncodingException
;
import
java.net.URLDecoder
;
import
java.net.URLEncoder
;
import
java.util.HashMap
;
import
java.util.Map
;
import
java.util.TreeMap
;
/**
* @author wilmiam
...
...
@@ -172,11 +168,17 @@ public class ApiUtils {
*
* @param params
* @return
* @throws UnsupportedEncodingException
*/
public
static
String
decode
(
String
params
)
throws
UnsupportedEncodingException
{
params
=
URLDecoder
.
decode
(
params
,
"utf-8"
);
params
=
EncryptUtils
.
rsaDecodeByPrivateKey
(
params
,
RsaUtils
.
privateKey
);
public
static
String
decode
(
String
params
,
String
encryptType
)
{
if
(
StringUtils
.
isBlank
(
params
))
{
return
""
;
}
params
=
EncryptUtils
.
urlDecode
(
params
,
"UTF-8"
);
if
(
"RSA"
.
equals
(
encryptType
))
{
params
=
EncryptUtils
.
rsaDecodeByPrivateKey
(
params
,
RsaUtils
.
privateKey
);
}
else
{
params
=
EncryptUtils
.
base64Decode
(
params
);
}
return
params
;
}
...
...
@@ -187,14 +189,17 @@ public class ApiUtils {
*
* @param params
* @return
* @throws UnsupportedEncodingException
*/
public
static
String
encode
(
String
params
)
throws
UnsupportedEncodingException
{
params
=
EncryptUtils
.
rsaDecodeByPrivateKey
(
params
,
RsaUtils
.
publicKey
);
public
static
String
encode
(
String
params
,
String
encryptType
)
{
if
(
StringUtils
.
isBlank
(
params
))
{
return
""
;
}
params
=
URLEncoder
.
encode
(
params
,
"utf-8"
);
if
(
"RSA"
.
equals
(
encryptType
))
{
params
=
EncryptUtils
.
rsaDecodeByPrivateKey
(
params
,
RsaUtils
.
publicKey
);
}
else
{
params
=
EncryptUtils
.
base64Decode
(
params
);
}
params
=
EncryptUtils
.
urlEncode
(
params
,
"UTF-8"
);
return
params
;
}
...
...
@@ -203,16 +208,11 @@ public class ApiUtils {
* <p>
* 2017年3月15日 下午3:14:27
*
* @param
paramMaps
* @param
content
* @return
*/
public
static
String
getSign
(
TreeMap
<
String
,
String
>
paramMaps
)
{
// 原始请求串
StringBuilder
src
=
new
StringBuilder
();
for
(
Map
.
Entry
<
String
,
String
>
entry
:
paramMaps
.
entrySet
())
{
src
.
append
(
entry
.
getKey
()).
append
(
"="
).
append
(
entry
.
getValue
()).
append
(
"&"
);
}
return
MD5
.
create
().
digestHex
(
src
.
toString
());
public
static
String
getSign
(
String
content
)
{
return
MD5
.
create
().
digestHex
(
content
).
toUpperCase
();
}
}
xxx-common-utils/src/main/java/com/zq/common/constant/Cloud
Constant.java
→
logging-server/src/main/java/com/zq/logging/constant/
Constant.java
View file @
e67bc01a
...
...
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
com
.
zq
.
common
.
constant
;
package
com
.
zq
.
logging
.
constant
;
/**
* 常用静态常量
...
...
@@ -21,7 +21,7 @@ package com.zq.common.constant;
* @author Zheng Jie
* @date 2018-12-26
*/
public
class
C
loudC
onstant
{
public
class
Constant
{
/**
* 用于IP定位转换
...
...
logging-server/src/main/java/com/zq/logging/utils/RequestUtils.java
View file @
e67bc01a
...
...
@@ -3,8 +3,8 @@ package com.zq.logging.utils;
import
cn.hutool.http.HttpUtil
;
import
cn.hutool.json.JSONUtil
;
import
com.zq.common.config.base.SpringContextHolder
;
import
com.zq.common.constant.CloudConstant
;
import
com.zq.logging.config.AdminProperties
;
import
com.zq.logging.constant.Constant
;
import
net.dreamlu.mica.ip2region.core.Ip2regionSearcher
;
import
net.dreamlu.mica.ip2region.core.IpInfo
;
import
nl.basjes.parse.useragent.UserAgent
;
...
...
@@ -46,7 +46,7 @@ public class RequestUtils {
* 根据ip获取详细地址
*/
public
static
String
getHttpCityInfo
(
String
ip
)
{
String
api
=
String
.
format
(
C
loudC
onstant
.
Url
.
IP_URL
,
ip
);
String
api
=
String
.
format
(
Constant
.
Url
.
IP_URL
,
ip
);
cn
.
hutool
.
json
.
JSONObject
object
=
JSONUtil
.
parseObj
(
HttpUtil
.
get
(
api
));
return
object
.
get
(
"addr"
,
String
.
class
);
}
...
...
user-server/src/main/java/com/zq/user/config/SpringSecurityConfig.java
View file @
e67bc01a
...
...
@@ -18,7 +18,6 @@ package com.zq.user.config;
import
com.zq.common.annotation.AnonymousAccess
;
import
com.zq.common.config.redis.RedisUtils
;
import
com.zq.common.config.security.SecurityProperties
;
import
com.zq.common.config.security.TokenProvider
;
import
com.zq.common.utils.RequestMethodEnum
;
import
lombok.RequiredArgsConstructor
;
import
org.springframework.context.ApplicationContext
;
...
...
user-server/src/main/java/com/zq/user/config/TokenConfigurer.java
View file @
e67bc01a
...
...
@@ -17,7 +17,6 @@ package com.zq.user.config;
import
com.zq.common.config.redis.RedisUtils
;
import
com.zq.common.config.security.SecurityProperties
;
import
com.zq.common.config.security.TokenProvider
;
import
lombok.RequiredArgsConstructor
;
import
org.springframework.security.config.annotation.SecurityConfigurerAdapter
;
import
org.springframework.security.config.annotation.web.builders.HttpSecurity
;
...
...
user-server/src/main/java/com/zq/user/config/TokenFilter.java
View file @
e67bc01a
...
...
@@ -19,7 +19,6 @@ import cn.hutool.core.util.StrUtil;
import
com.zq.common.config.redis.BaseCacheKeys
;
import
com.zq.common.config.redis.RedisUtils
;
import
com.zq.common.config.security.SecurityProperties
;
import
com.zq.common.config.security.TokenProvider
;
import
com.zq.common.context.ContextUtils
;
import
com.zq.common.vo.OnlineUserDto
;
import
io.jsonwebtoken.ExpiredJwtException
;
...
...
user-server/src/main/java/com/zq/user/config/TokenProvider.java
View file @
e67bc01a
//
package com.zq.user.config;/*
//
* Copyright 2019-2020 Zheng Jie
//
*
//
* Licensed under the Apache License, Version 2.0 (the "License");
//
* you may not use this file except in compliance with the License.
//
* You may obtain a copy of the License at
//
*
//
* http://www.apache.org/licenses/LICENSE-2.0
//
*
//
* Unless required by applicable law or agreed to in writing, software
//
* distributed under the License is distributed on an "AS IS" BASIS,
//
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
* See the License for the specific language governing permissions and
//
* limitations under the License.
//
*/
//
//
import cn.hutool.core.date.DateField;
//
import cn.hutool.core.date.DateUtil;
//
import cn.hutool.core.util.IdUtil;
//
import cn.hutool.core.util.ObjectUtil;
//
import com.zq.common.config.redis.RedisUtils;
//
import com.zq.common.config.security.SecurityProperties;
//
import io.jsonwebtoken.Claims;
//
import io.jsonwebtoken.Jwts;
//
import io.jsonwebtoken.SignatureAlgorithm;
//
import lombok.RequiredArgsConstructor;
//
import lombok.extern.slf4j.Slf4j;
//
import org.apache.commons.lang3.StringUtils;
//
import org.springframework.beans.factory.InitializingBean;
//
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
//
import org.springframework.security.core.Authentication;
//
import org.springframework.security.core.GrantedAuthority;
//
import org.springframework.security.core.authority.SimpleGrantedAuthority;
//
import org.springframework.security.core.userdetails.User;
//
import org.springframework.stereotype.Component;
//
//
import javax.crypto.spec.SecretKeySpec;
//
import javax.servlet.http.HttpServletRequest;
//
import javax.xml.bind.DatatypeConverter;
//
import java.security.Key;
//
import java.util.Arrays;
//
import java.util.Collection;
//
import java.util.Collections;
//
import java.util.Date;
//
import java.util.concurrent.TimeUnit;
//
import java.util.stream.Collectors;
//
/
/ /
**
//
* @author /
//
*/
//
@Slf4j
//
@Component
//
@RequiredArgsConstructor
//
public class TokenProvider implements InitializingBean {
//
//
private final RedisUtils redisUtils;
//
private final SecurityProperties properties;
//
//
public static final String AUTHORITIES_KEY = "auth";
//
private static Key key;
//
private static SignatureAlgorithm signatureAlgorithm;
//
//
@Override
//
public void afterPropertiesSet() {
//
signatureAlgorithm = SignatureAlgorithm.HS512;
//
byte[] keyBytes = DatatypeConverter.parseBase64Binary(properties.getBase64Secret());
//
key = new SecretKeySpec(keyBytes, signatureAlgorithm.getJcaName());
//
}
//
//
public static String createToken(Authentication authentication) {
//
String authorities = authentication.getAuthorities().stream()
//
.map(GrantedAuthority::getAuthority)
//
.collect(Collectors.joining(","));
//
//
return Jwts.builder()
//
.setSubject(authentication.getName())
//
.claim(AUTHORITIES_KEY, authorities)
//
.signWith(signatureAlgorithm, key)
//
// 加入ID确保生成的 Token 都不一致
//
.setId(IdUtil.simpleUUID())
//
.compact();
//
}
//
//
public Claims getClaims(String token) {
//
return Jwts.parser()
//
.setSigningKey(DatatypeConverter.parseBase64Binary(properties.getBase64Secret()))
//
.parseClaimsJws(token)
//
.getBody();
//
}
//
//
public Authentication getAuthentication(String token) {
//
Claims claims = getClaims(token);
//
//
// fix bug: 当前用户如果没有任何权限时,在输入用户名后,刷新验证码会抛IllegalArgumentException
//
Object authoritiesStr = claims.get(AUTHORITIES_KEY);
//
Collection<? extends GrantedAuthority> authorities =
//
ObjectUtil.isNotEmpty(authoritiesStr) ?
//
Arrays.stream(authoritiesStr.toString().split(","))
//
.map(SimpleGrantedAuthority::new)
//
.collect(Collectors.toList()) : Collections.emptyList();
//
//
User principal = new User(claims.getSubject(), "******", authorities);
//
//
return new UsernamePasswordAuthenticationToken(principal, token, authorities);
//
}
//
//
/**
//
* @param token 需要检查的token
//
*/
//
public void checkRenewal(String token) {
//
// 判断是否续期token,计算token的过期时间
//
long time = redisUtils.getExpire(properties.getOnlineKey() + token) * 1000;
//
Date expireDate = DateUtil.offset(new Date(), DateField.MILLISECOND, (int) time);
//
// 判断当前时间与过期时间的时间差
//
long differ = expireDate.getTime() - System.currentTimeMillis();
//
// 如果在续期检查的范围内,则续期
//
if (differ <= properties.getDetect()) {
//
long renew = time + properties.getRenew();
//
redisUtils.expire(properties.getOnlineKey() + token, renew, TimeUnit.MILLISECONDS);
//
}
//
}
//
//
public String getToken(HttpServletRequest request) {
//
String bearerToken = request.getHeader(properties.getHeader());
//
if (StringUtils.isBlank(bearerToken)) {
//
return null;
//
}
//
if (bearerToken.startsWith(properties.getTokenStartWith())) {
//
// 去掉令牌前缀
//
return bearerToken.replace(properties.getTokenStartWith(), "");
//
} else {
//
log.debug("非法Token:{}", bearerToken);
//
}
//
return null;
//
}
//
//
}
package
com
.
zq
.
user
.
config
;
/*
* Copyright 2019-2020 Zheng Jie
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import
cn.hutool.core.date.DateField
;
import
cn.hutool.core.date.DateUtil
;
import
cn.hutool.core.util.IdUtil
;
import
cn.hutool.core.util.ObjectUtil
;
import
com.zq.common.config.redis.RedisUtils
;
import
com.zq.common.config.security.SecurityProperties
;
import
io.jsonwebtoken.Claims
;
import
io.jsonwebtoken.Jwts
;
import
io.jsonwebtoken.SignatureAlgorithm
;
import
lombok.RequiredArgsConstructor
;
import
lombok.extern.slf4j.Slf4j
;
import
org.apache.commons.lang3.StringUtils
;
import
org.springframework.beans.factory.InitializingBean
;
import
org.springframework.security.authentication.UsernamePasswordAuthenticationToken
;
import
org.springframework.security.core.Authentication
;
import
org.springframework.security.core.GrantedAuthority
;
import
org.springframework.security.core.authority.SimpleGrantedAuthority
;
import
org.springframework.security.core.userdetails.User
;
import
org.springframework.stereotype.Component
;
import
javax.crypto.spec.SecretKeySpec
;
import
javax.servlet.http.HttpServletRequest
;
import
javax.xml.bind.DatatypeConverter
;
import
java.security.Key
;
import
java.util.Arrays
;
import
java.util.Collection
;
import
java.util.Collections
;
import
java.util.Date
;
import
java.util.concurrent.TimeUnit
;
import
java.util.stream.Collectors
;
/**
* @author /
*/
@Slf4j
@Component
@RequiredArgsConstructor
public
class
TokenProvider
implements
InitializingBean
{
private
final
RedisUtils
redisUtils
;
private
final
SecurityProperties
properties
;
public
static
final
String
AUTHORITIES_KEY
=
"auth"
;
private
static
Key
key
;
private
static
SignatureAlgorithm
signatureAlgorithm
;
@Override
public
void
afterPropertiesSet
()
{
signatureAlgorithm
=
SignatureAlgorithm
.
HS512
;
byte
[]
keyBytes
=
DatatypeConverter
.
parseBase64Binary
(
properties
.
getBase64Secret
());
key
=
new
SecretKeySpec
(
keyBytes
,
signatureAlgorithm
.
getJcaName
());
}
public
static
String
createToken
(
Authentication
authentication
)
{
String
authorities
=
authentication
.
getAuthorities
().
stream
()
.
map
(
GrantedAuthority:
:
getAuthority
)
.
collect
(
Collectors
.
joining
(
","
));
return
Jwts
.
builder
()
.
setSubject
(
authentication
.
getName
())
.
claim
(
AUTHORITIES_KEY
,
authorities
)
.
signWith
(
signatureAlgorithm
,
key
)
// 加入ID确保生成的 Token 都不一致
.
setId
(
IdUtil
.
simpleUUID
())
.
compact
();
}
public
Claims
getClaims
(
String
token
)
{
return
Jwts
.
parser
()
.
setSigningKey
(
DatatypeConverter
.
parseBase64Binary
(
properties
.
getBase64Secret
()))
.
parseClaimsJws
(
token
)
.
getBody
();
}
public
Authentication
getAuthentication
(
String
token
)
{
Claims
claims
=
getClaims
(
token
);
// fix bug: 当前用户如果没有任何权限时,在输入用户名后,刷新验证码会抛IllegalArgumentException
Object
authoritiesStr
=
claims
.
get
(
AUTHORITIES_KEY
);
Collection
<?
extends
GrantedAuthority
>
authorities
=
ObjectUtil
.
isNotEmpty
(
authoritiesStr
)
?
Arrays
.
stream
(
authoritiesStr
.
toString
().
split
(
","
))
.
map
(
SimpleGrantedAuthority:
:
new
)
.
collect
(
Collectors
.
toList
())
:
Collections
.
emptyList
();
User
principal
=
new
User
(
claims
.
getSubject
(),
"******"
,
authorities
);
return
new
UsernamePasswordAuthenticationToken
(
principal
,
token
,
authorities
);
}
/**
* @param token 需要检查的token
*/
public
void
checkRenewal
(
String
token
)
{
// 判断是否续期token,计算token的过期时间
long
time
=
redisUtils
.
getExpire
(
properties
.
getOnlineKey
()
+
token
)
*
1000
;
Date
expireDate
=
DateUtil
.
offset
(
new
Date
(),
DateField
.
MILLISECOND
,
(
int
)
time
);
// 判断当前时间与过期时间的时间差
long
differ
=
expireDate
.
getTime
()
-
System
.
currentTimeMillis
();
// 如果在续期检查的范围内,则续期
if
(
differ
<=
properties
.
getDetect
())
{
long
renew
=
time
+
properties
.
getRenew
();
redisUtils
.
expire
(
properties
.
getOnlineKey
()
+
token
,
renew
,
TimeUnit
.
MILLISECONDS
);
}
}
public
String
getToken
(
HttpServletRequest
request
)
{
String
bearerToken
=
request
.
getHeader
(
properties
.
getHeader
());
if
(
StringUtils
.
isBlank
(
bearerToken
))
{
return
null
;
}
if
(
bearerToken
.
startsWith
(
properties
.
getTokenStartWith
()))
{
// 去掉令牌前缀
return
bearerToken
.
replace
(
properties
.
getTokenStartWith
(),
""
);
}
else
{
log
.
debug
(
"非法Token:{}"
,
bearerToken
);
}
return
null
;
}
}
xxx-common-utils/src/main/java/com/zq/common/config/base/UnifiedExceptionHandler.java
View file @
e67bc01a
package
com
.
zq
.
common
.
config
.
base
;
import
com.zq.common.constant.FeignHeader
;
import
com.zq.common.constant.SystemName
;
import
com.zq.common.exception.BusinessException
;
import
com.zq.common.utils.ThrowableUtil
;
import
com.zq.common.vo.ResultVo
;
...
...
@@ -94,8 +96,9 @@ public class UnifiedExceptionHandler {
@ExceptionHandler
(
DataAccessException
.
class
)
public
ResultVo
handleDataAccessException
(
DataAccessException
ex
,
HttpServletRequest
request
)
{
log
.
error
(
">> 访问数据失败 "
+
request
.
getRequestURI
(),
ex
);
String
header
=
request
.
getHeader
(
FeignHeader
.
SERVER_NAME
);
String
error
=
"服务器繁忙"
;
if
(
request
.
getRequestURI
().
contains
(
"/app/"
))
{
if
(
StringUtils
.
isNotBlank
(
header
)
&&
SystemName
.
API
.
equals
(
header
))
{
error
=
ThrowableUtil
.
getStackTrace
(
ex
);
}
return
ResultVo
.
fail
(
HttpStatus
.
INTERNAL_SERVER_ERROR
.
value
(),
error
);
...
...
@@ -104,8 +107,9 @@ public class UnifiedExceptionHandler {
@ExceptionHandler
(
value
=
Exception
.
class
)
public
ResultVo
defaultErrorHandler
(
Exception
ex
,
HttpServletRequest
request
)
{
log
.
error
(
">> 服务器内部错误 "
+
request
.
getRequestURI
(),
ex
);
String
header
=
request
.
getHeader
(
FeignHeader
.
SERVER_NAME
);
String
error
=
"服务器繁忙"
;
if
(
request
.
getRequestURI
().
contains
(
"/app/"
))
{
if
(
StringUtils
.
isNotBlank
(
header
)
&&
SystemName
.
API
.
equals
(
header
))
{
error
=
ThrowableUtil
.
getStackTrace
(
ex
);
}
return
ResultVo
.
fail
(
HttpStatus
.
INTERNAL_SERVER_ERROR
.
value
(),
error
);
...
...
xxx-common-utils/src/main/java/com/zq/common/config/security/TokenProvider.java
deleted
100644 → 0
View file @
4809b2b9
package
com
.
zq
.
common
.
config
.
security
;
/*
* Copyright 2019-2020 Zheng Jie
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import
cn.hutool.core.date.DateField
;
import
cn.hutool.core.date.DateUtil
;
import
cn.hutool.core.util.IdUtil
;
import
cn.hutool.core.util.ObjectUtil
;
import
com.zq.common.config.redis.RedisUtils
;
import
io.jsonwebtoken.Claims
;
import
io.jsonwebtoken.Jwts
;
import
io.jsonwebtoken.SignatureAlgorithm
;
import
lombok.RequiredArgsConstructor
;
import
lombok.extern.slf4j.Slf4j
;
import
org.apache.commons.lang3.StringUtils
;
import
org.springframework.beans.factory.InitializingBean
;
import
org.springframework.security.authentication.UsernamePasswordAuthenticationToken
;
import
org.springframework.security.core.Authentication
;
import
org.springframework.security.core.GrantedAuthority
;
import
org.springframework.security.core.authority.SimpleGrantedAuthority
;
import
org.springframework.security.core.userdetails.User
;
import
org.springframework.stereotype.Component
;
import
javax.crypto.spec.SecretKeySpec
;
import
javax.servlet.http.HttpServletRequest
;
import
javax.xml.bind.DatatypeConverter
;
import
java.security.Key
;
import
java.util.Arrays
;
import
java.util.Collection
;
import
java.util.Collections
;
import
java.util.Date
;
import
java.util.concurrent.TimeUnit
;
import
java.util.stream.Collectors
;
/**
* @author /
*/
@Slf4j
@Component
@RequiredArgsConstructor
public
class
TokenProvider
implements
InitializingBean
{
private
final
RedisUtils
redisUtils
;
private
final
SecurityProperties
properties
;
public
static
final
String
AUTHORITIES_KEY
=
"auth"
;
private
static
Key
key
;
private
static
SignatureAlgorithm
signatureAlgorithm
;
@Override
public
void
afterPropertiesSet
()
{
signatureAlgorithm
=
SignatureAlgorithm
.
HS512
;
byte
[]
keyBytes
=
DatatypeConverter
.
parseBase64Binary
(
properties
.
getBase64Secret
());
key
=
new
SecretKeySpec
(
keyBytes
,
signatureAlgorithm
.
getJcaName
());
}
public
static
String
createToken
(
Authentication
authentication
)
{
String
authorities
=
authentication
.
getAuthorities
().
stream
()
.
map
(
GrantedAuthority:
:
getAuthority
)
.
collect
(
Collectors
.
joining
(
","
));
return
Jwts
.
builder
()
.
setSubject
(
authentication
.
getName
())
.
claim
(
AUTHORITIES_KEY
,
authorities
)
.
signWith
(
signatureAlgorithm
,
key
)
// 加入ID确保生成的 Token 都不一致
.
setId
(
IdUtil
.
simpleUUID
())
.
compact
();
}
public
Claims
getClaims
(
String
token
)
{
return
Jwts
.
parser
()
.
setSigningKey
(
DatatypeConverter
.
parseBase64Binary
(
properties
.
getBase64Secret
()))
.
parseClaimsJws
(
token
)
.
getBody
();
}
public
Authentication
getAuthentication
(
String
token
)
{
Claims
claims
=
getClaims
(
token
);
// fix bug: 当前用户如果没有任何权限时,在输入用户名后,刷新验证码会抛IllegalArgumentException
Object
authoritiesStr
=
claims
.
get
(
AUTHORITIES_KEY
);
Collection
<?
extends
GrantedAuthority
>
authorities
=
ObjectUtil
.
isNotEmpty
(
authoritiesStr
)
?
Arrays
.
stream
(
authoritiesStr
.
toString
().
split
(
","
))
.
map
(
SimpleGrantedAuthority:
:
new
)
.
collect
(
Collectors
.
toList
())
:
Collections
.
emptyList
();
User
principal
=
new
User
(
claims
.
getSubject
(),
"******"
,
authorities
);
return
new
UsernamePasswordAuthenticationToken
(
principal
,
token
,
authorities
);
}
/**
* @param token 需要检查的token
*/
public
void
checkRenewal
(
String
token
)
{
// 判断是否续期token,计算token的过期时间
long
time
=
redisUtils
.
getExpire
(
properties
.
getOnlineKey
()
+
token
)
*
1000
;
Date
expireDate
=
DateUtil
.
offset
(
new
Date
(),
DateField
.
MILLISECOND
,
(
int
)
time
);
// 判断当前时间与过期时间的时间差
long
differ
=
expireDate
.
getTime
()
-
System
.
currentTimeMillis
();
// 如果在续期检查的范围内,则续期
if
(
differ
<=
properties
.
getDetect
())
{
long
renew
=
time
+
properties
.
getRenew
();
redisUtils
.
expire
(
properties
.
getOnlineKey
()
+
token
,
renew
,
TimeUnit
.
MILLISECONDS
);
}
}
public
String
getToken
(
HttpServletRequest
request
)
{
String
bearerToken
=
request
.
getHeader
(
properties
.
getHeader
());
if
(
StringUtils
.
isBlank
(
bearerToken
))
{
return
null
;
}
if
(
bearerToken
.
startsWith
(
properties
.
getTokenStartWith
()))
{
// 去掉令牌前缀
return
bearerToken
.
replace
(
properties
.
getTokenStartWith
(),
""
);
}
else
{
log
.
debug
(
"非法Token:{}"
,
bearerToken
);
}
return
null
;
}
}
xxx-common-utils/src/main/java/com/zq/common/constant/FeignHeader.java
0 → 100644
View file @
e67bc01a
package
com
.
zq
.
common
.
constant
;
/**
* feign要添加的请求头
*
* @author wilmiam
* @since 2021/9/6 9:43
*/
public
class
FeignHeader
{
/**
* feign添加服务名的请求头字段
*/
public
static
final
String
SERVER_NAME
=
"X-Server-Name"
;
/**
* feign添加api-token的请求头字段
*/
public
static
final
String
API_TOKEN
=
"X-Api-Token"
;
}
xxx-common-utils/src/main/java/com/zq/common/constant/SystemName.java
0 → 100644
View file @
e67bc01a
package
com
.
zq
.
common
.
constant
;
/**
* @author wilmiam
* @since 2021-09-06 09:16
*/
public
class
SystemName
{
public
static
final
String
ADMIN
=
"admin"
;
public
static
final
String
API
=
"api"
;
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment