Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
X
xyqb-user2
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
head_group
xyqb-user2
Commits
c39f7a6b
Commit
c39f7a6b
authored
Oct 31, 2017
by
技术部-任文超
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
提交单次令牌发放的http服务和单词令牌校验切面+注解
parent
1f7db0de
Changes
7
Show whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
257 additions
and
0 deletions
+257
-0
Constants.java
src/main/java/cn/quantgroup/xyqb/Constants.java
+1
-0
SingleTokenValidateAdvisor.java
...ntgroup/xyqb/aspect/token/SingleTokenValidateAdvisor.java
+161
-0
SingleTokenValidator.java
...cn/quantgroup/xyqb/aspect/token/SingleTokenValidator.java
+15
-0
SingleTokenController.java
...xyqb/controller/internal/token/SingleTokenController.java
+54
-0
xyqb.properties
src/main/resources/config/dev/xyqb.properties
+2
-0
xyqb.properties
src/main/resources/config/test/xyqb.properties
+2
-0
CommonTests.java
src/test/java/CommonTests.java
+22
-0
No files found.
src/main/java/cn/quantgroup/xyqb/Constants.java
View file @
c39f7a6b
...
@@ -12,6 +12,7 @@ public interface Constants {
...
@@ -12,6 +12,7 @@ public interface Constants {
String
PASSWORD_SALT
=
"_lkb"
;
String
PASSWORD_SALT
=
"_lkb"
;
String
IMAGE_CAPTCHA_KEY
=
"img_captcha:"
;
String
IMAGE_CAPTCHA_KEY
=
"img_captcha:"
;
String
TOKEN_SINGLE_KEY_FOR_PHONE
=
"token_single:for_phone:"
;
String
REDIS_CAPTCHA_KEY
=
"auth:"
;
String
REDIS_CAPTCHA_KEY
=
"auth:"
;
String
REDIS_CAPTCHA_KEY_PATTERN
=
REDIS_CAPTCHA_KEY
+
IMAGE_CAPTCHA_KEY
+
"*"
;
String
REDIS_CAPTCHA_KEY_PATTERN
=
REDIS_CAPTCHA_KEY
+
IMAGE_CAPTCHA_KEY
+
"*"
;
...
...
src/main/java/cn/quantgroup/xyqb/aspect/token/SingleTokenValidateAdvisor.java
0 → 100644
View file @
c39f7a6b
package
cn
.
quantgroup
.
xyqb
.
aspect
.
token
;
import
cn.quantgroup.xyqb.Constants
;
import
cn.quantgroup.xyqb.exception.VerificationCodeErrorException
;
import
cn.quantgroup.xyqb.model.JsonResult
;
import
cn.quantgroup.xyqb.util.ValidationUtil
;
import
org.apache.commons.codec.binary.Base64
;
import
org.apache.commons.lang3.StringUtils
;
import
org.aspectj.lang.ProceedingJoinPoint
;
import
org.aspectj.lang.annotation.Around
;
import
org.aspectj.lang.annotation.Aspect
;
import
org.aspectj.lang.annotation.Pointcut
;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.beans.factory.annotation.Qualifier
;
import
org.springframework.beans.factory.annotation.Value
;
import
org.springframework.data.redis.core.RedisTemplate
;
import
org.springframework.stereotype.Component
;
import
org.springframework.web.context.request.RequestContextHolder
;
import
org.springframework.web.context.request.ServletRequestAttributes
;
import
javax.servlet.http.HttpServletRequest
;
import
java.io.UnsupportedEncodingException
;
import
java.util.HashMap
;
import
java.util.Map
;
import
java.util.Objects
;
/**
* 单次令牌校验切面
*
* @author 任文超
* @version 1.0.0
* @since 2017-10-31
*/
@Aspect
@Component
public
class
SingleTokenValidateAdvisor
{
private
static
final
Logger
LOGGER
=
LoggerFactory
.
getLogger
(
SingleTokenValidateAdvisor
.
class
);
@Autowired
@Qualifier
(
"stringRedisTemplate"
)
private
RedisTemplate
<
String
,
String
>
redisTemplate
;
/**
* 自动化测试忽略单次令牌校验
*/
@Value
(
"${xyqb.auth.singletoken.autotest.enable:false}"
)
private
boolean
autoTestSingleTokenEnabled
;
/**
* 单次令牌校验切面
*/
@Pointcut
(
"@annotation(cn.quantgroup.xyqb.aspect.token.SingleTokenValidator)"
)
private
void
needSingleTokenValidate
()
{
}
/**
* 在受单次令牌保护的接口方法执行前, 执行单次令牌校验
*
* @throws Throwable
*/
@Around
(
"needSingleTokenValidate()"
)
private
Object
doSingleTokenValidate
(
ProceedingJoinPoint
pjp
)
throws
Throwable
{
if
(
autoTestSingleTokenEnabled
)
{
return
pjp
.
proceed
();
}
boolean
checkTokenForPhone
=
checkTokenForPhone
();
if
(!
checkTokenForPhone
)
{
return
JsonResult
.
buildSuccessResult
(
"Token过期,请重新请求"
,
""
,
2L
);
}
return
pjp
.
proceed
();
}
/**
* 校验按手机号分发的单次令牌
* @return True or False
*/
private
boolean
checkTokenForPhone
()
{
HttpServletRequest
request
=
((
ServletRequestAttributes
)
RequestContextHolder
.
getRequestAttributes
()).
getRequest
();
Map
<
String
,
String
>
phoneTokenMap
=
getHeaderParam
(
request
);
if
(
phoneTokenMap
==
null
||
phoneTokenMap
.
isEmpty
()){
return
false
;
}
// 当前用户手机号
String
phoneNo
=
phoneTokenMap
.
get
(
"phoneNo"
);
// 当前请求的SingleToken
String
requestToken
=
phoneTokenMap
.
get
(
"requestToken"
);
if
(
StringUtils
.
isBlank
(
phoneNo
)
||
StringUtils
.
isBlank
(
requestToken
)){
return
false
;
}
final
String
key
=
Constants
.
TOKEN_SINGLE_KEY_FOR_PHONE
+
phoneNo
;
String
singleToken
=
redisTemplate
.
opsForValue
().
get
(
key
);
// SingleToken不应为空值(空白、空格、null)
if
(
StringUtils
.
isBlank
(
singleToken
))
{
// 修正规则
if
(
redisTemplate
.
hasKey
(
key
)){
redisTemplate
.
delete
(
key
);
}
return
false
;
}
boolean
valid
=
Objects
.
equals
(
singleToken
,
requestToken
);
// SingleToken校验正确时删除key
if
(
valid
)
{
redisTemplate
.
delete
(
key
);
}
else
{
LOGGER
.
info
(
"Token过期,请重新请求, token_single:for_phone:={}, requestToken={}, clientIp={}"
,
phoneNo
,
requestToken
,
request
.
getRemoteAddr
());
}
return
valid
;
}
/**
* 单次令牌参数解析
*
* @param request 当前请求,其首部行必须包含形如【SingleToken 13461067662:0123456789abcdef】的UTF-8编码的Base64加密参数
* @return 令牌参数Map 或 null
*/
private
Map
<
String
,
String
>
getHeaderParam
(
HttpServletRequest
request
)
{
String
verificationHeader
=
"SingleToken "
;
String
credential
=
request
.
getHeader
(
"authorization"
);
if
(
StringUtils
.
isBlank
(
credential
)
||
!
credential
.
startsWith
(
verificationHeader
))
{
LOGGER
.
info
(
"令牌参数无效, credential:{}"
,
credential
);
return
null
;
}
credential
=
credential
.
substring
(
verificationHeader
.
length
(),
credential
.
length
());
boolean
headerParamValid
=
true
;
byte
[]
buf
=
Base64
.
decodeBase64
(
credential
);
try
{
credential
=
new
String
(
buf
,
"UTF-8"
);
}
catch
(
UnsupportedEncodingException
e
)
{
headerParamValid
=
false
;
LOGGER
.
error
(
"不支持的编码{}."
,
credential
,
e
);
}
if
(!
headerParamValid
){
LOGGER
.
info
(
"令牌参数无效, credential:{}"
,
credential
);
return
null
;
}
String
[]
credentialArr
=
credential
.
split
(
":"
);
headerParamValid
=
headerParamValid
&&
credentialArr
.
length
==
2
;
if
(!
headerParamValid
)
{
LOGGER
.
info
(
"令牌参数无效, credential:{}"
,
credential
);
return
null
;
}
// 当前用户手机号
String
phoneNo
=
credentialArr
[
0
];
// 当前请求的SingleToken
String
requestToken
=
credentialArr
[
1
];
headerParamValid
=
headerParamValid
&&
ValidationUtil
.
validatePhoneNo
(
phoneNo
);
if
(!
headerParamValid
)
{
LOGGER
.
info
(
"令牌参数无效, credential:{}"
,
credential
);
return
null
;
}
LOGGER
.
info
(
"获取单次令牌, phoneNo:{}, requestToken:{}"
,
phoneNo
,
requestToken
);
Map
<
String
,
String
>
phoneTokenMap
=
new
HashMap
<
String
,
String
>(
2
);
phoneTokenMap
.
put
(
"phoneNo"
,
phoneNo
);
phoneTokenMap
.
put
(
"requestToken"
,
requestToken
);
return
phoneTokenMap
;
}
}
src/main/java/cn/quantgroup/xyqb/aspect/token/SingleTokenValidator.java
0 → 100644
View file @
c39f7a6b
package
cn
.
quantgroup
.
xyqb
.
aspect
.
token
;
import
java.lang.annotation.*
;
/**
* 需要单次令牌校验标记
* @author 任文超
* @version 1.0.0
* @since 2017-10-31
*/
@Documented
@Target
(
ElementType
.
METHOD
)
@Retention
(
RetentionPolicy
.
RUNTIME
)
public
@interface
SingleTokenValidator
{
}
src/main/java/cn/quantgroup/xyqb/controller/internal/token/SingleTokenController.java
0 → 100644
View file @
c39f7a6b
package
cn
.
quantgroup
.
xyqb
.
controller
.
internal
.
token
;
import
cn.quantgroup.xyqb.Constants
;
import
cn.quantgroup.xyqb.controller.IBaseController
;
import
cn.quantgroup.xyqb.model.JsonResult
;
import
org.apache.commons.lang3.StringUtils
;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.beans.factory.annotation.Qualifier
;
import
org.springframework.data.redis.core.RedisTemplate
;
import
org.springframework.web.bind.annotation.ModelAttribute
;
import
org.springframework.web.bind.annotation.RequestMapping
;
import
org.springframework.web.bind.annotation.RestController
;
import
javax.servlet.http.HttpServletRequest
;
import
java.util.UUID
;
import
java.util.concurrent.TimeUnit
;
/**
* 发放单次令牌
*
* @author 任文超
* @version 1.0.0
* @since 2017-10-31
*/
@RestController
@RequestMapping
(
"/token"
)
public
class
SingleTokenController
implements
IBaseController
{
private
static
final
Logger
LOGGER
=
LoggerFactory
.
getLogger
(
SingleTokenController
.
class
);
private
static
final
Long
ONE_HOUR
=
1
*
60
*
60L
;
@Autowired
@Qualifier
(
"stringRedisTemplate"
)
private
RedisTemplate
<
String
,
String
>
redisTemplate
;
/**
* 向指定用户账号(手机号)发放一枚SingleToken
* @param phoneNo 用户账号(手机号)
* @return 单次令牌
*/
@RequestMapping
(
value
=
"/single"
)
public
JsonResult
newSingleToken
(
HttpServletRequest
request
,
@ModelAttribute
(
"phoneNo"
)
String
phoneNo
)
{
if
(
StringUtils
.
isBlank
(
phoneNo
)){
return
JsonResult
.
buildErrorStateResult
(
""
,
"fail"
);
}
String
singleToken
=
UUID
.
randomUUID
().
toString
();
final
String
key
=
Constants
.
TOKEN_SINGLE_KEY_FOR_PHONE
+
phoneNo
;
redisTemplate
.
opsForValue
().
set
(
key
,
singleToken
,
ONE_HOUR
,
TimeUnit
.
SECONDS
);
return
JsonResult
.
buildSuccessResult
(
""
,
singleToken
);
}
}
src/main/resources/config/dev/xyqb.properties
View file @
c39f7a6b
...
@@ -57,6 +57,8 @@ jr58.notify.userinfo=http://xfd.test.58v5.cn/customer/quantgroup_user_info
...
@@ -57,6 +57,8 @@ jr58.notify.userinfo=http://xfd.test.58v5.cn/customer/quantgroup_user_info
# 图形验证码
# 图形验证码
# 是否启用超级验证码 "__SUPERQG__", 用于测试环境自动化测试, 线上环境可忽略此参数
# 是否启用超级验证码 "__SUPERQG__", 用于测试环境自动化测试, 线上环境可忽略此参数
xyqb.auth.captcha.super.enable
=
1
xyqb.auth.captcha.super.enable
=
1
# 单次令牌验证, 用于测试环境自动化测试, 线上环境可忽略此参数
xyqb.auth.singletoken.autotest.enable
=
true
#首参数校验
#首参数校验
xyqb.fplock.limit.byhour
=
3
xyqb.fplock.limit.byhour
=
3
...
...
src/main/resources/config/test/xyqb.properties
View file @
c39f7a6b
...
@@ -39,6 +39,8 @@ jr58.notify.userinfo=http://xfd.test.58v5.cn/customer/quantgroup_user_info
...
@@ -39,6 +39,8 @@ jr58.notify.userinfo=http://xfd.test.58v5.cn/customer/quantgroup_user_info
# 图形验证码
# 图形验证码
# 是否启用超级验证码 "__SUPERQG__", 用于测试环境自动化测试, 线上环境可忽略此参数
# 是否启用超级验证码 "__SUPERQG__", 用于测试环境自动化测试, 线上环境可忽略此参数
xyqb.auth.captcha.super.enable
=
1
xyqb.auth.captcha.super.enable
=
1
# 单次令牌验证, 用于测试环境自动化测试, 线上环境可忽略此参数
xyqb.auth.singletoken.autotest.enable
=
true
#首参数校验
#首参数校验
xyqb.fplock.limit.byhour
=
3
xyqb.fplock.limit.byhour
=
3
xyqb.fplock.limit.byday
=
5
xyqb.fplock.limit.byday
=
5
...
...
src/test/java/CommonTests.java
0 → 100644
View file @
c39f7a6b
import
org.junit.Assert
;
import
org.junit.Test
;
import
org.junit.runner.RunWith
;
import
org.springframework.test.context.junit4.SpringJUnit4ClassRunner
;
public
class
CommonTests
{
@Test
public
void
compileBooleanAndNull
()
{
Assert
.
assertFalse
(
null
instanceof
Boolean
);
Assert
.
assertNotEquals
(
null
,
Boolean
.
TRUE
);
Assert
.
assertNotEquals
(
null
,
Boolean
.
FALSE
);
}
@Test
public
void
print
()
{
System
.
out
.
println
(
null
instanceof
Boolean
);
System
.
out
.
println
(
Boolean
.
TRUE
.
equals
(
null
));
System
.
out
.
println
(
Boolean
.
FALSE
.
equals
(
null
));
}
}
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