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
c88d1ea1
Commit
c88d1ea1
authored
Jan 24, 2018
by
贷前—徐菲
Browse files
Options
Browse Files
Download
Plain Diff
Merge remote-tracking branch 'origin/master'
parents
865c856e
e92fa962
Changes
11
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
128 additions
and
458 deletions
+128
-458
Bootstrap.java
src/main/java/cn/quantgroup/xyqb/Bootstrap.java
+6
-1
IpValidateAdvisor.java
.../quantgroup/xyqb/aspect/accessable/IpValidateAdvisor.java
+0
-8
CaptchaNewValidateAdvisor.java
...tgroup/xyqb/aspect/captcha/CaptchaNewValidateAdvisor.java
+0
-167
CaptchaNewValidator.java
...n/quantgroup/xyqb/aspect/captcha/CaptchaNewValidator.java
+0
-13
CaptchaValidateAdvisor.java
...uantgroup/xyqb/aspect/captcha/CaptchaValidateAdvisor.java
+3
-8
PasswordFreeAccessValidateAdvisor.java
.../xyqb/aspect/limit/PasswordFreeAccessValidateAdvisor.java
+0
-7
WeChatController.java
...group/xyqb/controller/external/user/WeChatController.java
+105
-249
SmsController.java
...uantgroup/xyqb/controller/internal/sms/SmsController.java
+3
-4
UserDetailController.java
...p/xyqb/controller/internal/user/UserDetailController.java
+1
-1
UserAuthorizedServiceImpl.java
...oup/xyqb/service/auth/impl/UserAuthorizedServiceImpl.java
+9
-0
IPUtil.java
src/main/java/cn/quantgroup/xyqb/util/IPUtil.java
+1
-0
No files found.
src/main/java/cn/quantgroup/xyqb/Bootstrap.java
View file @
c88d1ea1
...
...
@@ -13,6 +13,7 @@ import org.springframework.context.ConfigurableApplicationContext;
import
org.springframework.context.annotation.ComponentScan
;
import
org.springframework.context.annotation.EnableAspectJAutoProxy
;
import
org.springframework.context.event.ContextRefreshedEvent
;
import
org.springframework.core.env.Environment
;
import
org.springframework.scheduling.annotation.EnableAsync
;
import
sun.misc.Signal
;
...
...
@@ -38,6 +39,10 @@ public class Bootstrap {
log
.
info
(
"server start..."
);
// 启用平滑退出功能
Signal
.
handle
(
new
Signal
(
"INT"
),
new
DefaultSignalHandler
(
run
));
Sentry
.
init
(
"http://13ef5642903a414c910f8d0e0a2c56ee:8b351ad1abf44de3b4c25f39105fb927@172.16.4.89:9000/6"
);
// 异常log捕获
Environment
environment
=
run
.
getBean
(
Environment
.
class
);
String
dsn
=
environment
.
getProperty
(
"dsn"
);
Sentry
.
init
(
dsn
);
}
}
src/main/java/cn/quantgroup/xyqb/aspect/accessable/IpValidateAdvisor.java
View file @
c88d1ea1
...
...
@@ -8,13 +8,11 @@ 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.Value
;
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.util.Objects
;
/**
* IP白名单检验
...
...
@@ -27,18 +25,12 @@ import java.util.Objects;
public
class
IpValidateAdvisor
{
private
static
final
Logger
LOGGER
=
LoggerFactory
.
getLogger
(
IpValidateAdvisor
.
class
);
@Value
(
"${configserver.disable}"
)
private
Integer
isDebug
;
@Pointcut
(
"execution(public * cn.quantgroup.xyqb.controller.external.user.InnerController.*(..)) || @annotation(cn.quantgroup.xyqb.aspect.accessable.IpValidator)"
)
private
void
whiteIpMatch
()
{
}
@Around
(
"whiteIpMatch()"
)
private
Object
doWhiteIpMatch
(
ProceedingJoinPoint
pjp
)
throws
Throwable
{
if
(
Objects
.
equals
(
isDebug
,
0
)){
return
pjp
.
proceed
();
}
HttpServletRequest
request
=
((
ServletRequestAttributes
)
RequestContextHolder
.
getRequestAttributes
()).
getRequest
();
// 客户端IP
String
clientIp
=
IPUtil
.
getRemoteIP
(
request
);
...
...
src/main/java/cn/quantgroup/xyqb/aspect/captcha/CaptchaNewValidateAdvisor.java
deleted
100644 → 0
View file @
865c856e
package
cn
.
quantgroup
.
xyqb
.
aspect
.
captcha
;
import
cn.quantgroup.xyqb.Constants
;
import
cn.quantgroup.xyqb.model.JsonResult
;
import
cn.quantgroup.xyqb.thirdparty.jcaptcha.AbstractManageableImageCaptchaService
;
import
cn.quantgroup.xyqb.util.IPUtil
;
import
com.octo.captcha.service.CaptchaServiceException
;
import
java.nio.charset.Charset
;
import
java.util.Optional
;
import
java.util.UUID
;
import
java.util.concurrent.TimeUnit
;
import
javax.servlet.http.HttpServletRequest
;
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
;
/**
* 类名称:CaptchaValidateAdvisor
* 类描述:
*
* @author 李宁
* @version 1.0.0 创建时间:15/11/17 14:49
*/
@Aspect
@Component
public
class
CaptchaNewValidateAdvisor
{
private
static
final
Logger
LOGGER
=
LoggerFactory
.
getLogger
(
CaptchaNewValidateAdvisor
.
class
);
private
static
final
String
SUPER_CAPTCHA_ID
=
UUID
.
nameUUIDFromBytes
(
"__QG_APPCLIENT_AGENT__"
.
getBytes
(
Charset
.
forName
(
"UTF-8"
))).
toString
();
private
static
final
String
SUPER_CAPTCHA
=
"__SUPERQG__"
;
@Autowired
@Qualifier
(
"stringRedisTemplate"
)
private
RedisTemplate
<
String
,
String
>
redisTemplate
;
@Autowired
@Qualifier
(
"customCaptchaService"
)
private
AbstractManageableImageCaptchaService
imageCaptchaService
;
/**
* 自动化测试忽略验证码
*/
@Value
(
"${xyqb.auth.captcha.autotest.enable:false}"
)
private
boolean
autoTestCaptchaEnabled
;
private
static
final
String
IMAGE_IP_COUNT
=
"image:ip"
;
private
static
final
String
IMAGE_PHONE_COUNT
=
"image:phone"
;
private
static
final
String
IMAGE_DEVICEID_COUNT
=
"image:deviceId:"
;
private
static
final
Long
FIVE_MIN
=
24
*
5L
;
/**
* 图形验证码切面
*/
@Pointcut
(
"@annotation(cn.quantgroup.xyqb.aspect.captcha.CaptchaNewValidator)"
)
private
void
needNewCaptchaValidate
()
{
}
/**
* 在受图形验证码保护的接口方法执行前, 执行图形验证码校验
* captchaId 图形验证码key
* captchaValue 图形验证码value
*
* @throws Throwable
*/
@Around
(
"needNewCaptchaValidate()"
)
private
Object
doCapchaValidate
(
ProceedingJoinPoint
pjp
)
throws
Throwable
{
HttpServletRequest
request
=
((
ServletRequestAttributes
)
RequestContextHolder
.
getRequestAttributes
()).
getRequest
();
String
registerFrom
=
Optional
.
ofNullable
(
request
.
getParameter
(
"registerFrom"
)).
orElse
(
""
);
String
captchaId
=
Optional
.
ofNullable
(
request
.
getParameter
(
"captchaId"
)).
orElse
(
""
);
String
captchaValue
=
request
.
getParameter
(
"captchaValue"
);
String
phoneNo
=
request
.
getParameter
(
"phoneNo"
);
String
deviceId
=
Optional
.
ofNullable
(
request
.
getParameter
(
"deviceId"
)).
orElse
(
""
);
String
clientIp
=
IPUtil
.
getRemoteIP
(
request
);
Long
countIP
=
countByClientId
(
clientIp
,
false
);
Long
countPhone
=
countPhone
(
phoneNo
);
Long
countDeviceId
=
countByClientId
(
deviceId
,
true
);
IPUtil
.
logIp
(
LOGGER
,
request
);
LOGGER
.
info
(
"使用图形验证码, registerFrom={}, clientIp={},手机号次数:{},设备次数:{},ip次数:{},phone:{}"
,
registerFrom
,
clientIp
,
countPhone
,
countDeviceId
,
countIP
,
phoneNo
);
//if (countIP > Constants.Image_Need_Count || countPhone > Constants.Image_Need_Count || countDeviceId > Constants.Image_Need_Count) {
if
(
true
){
if
(
shouldSkipCaptchaValidate
(
registerFrom
,
captchaId
,
captchaValue
))
{
LOGGER
.
info
(
"使用超级图形验证码校验, registerFrom={}, clientIp={}"
,
registerFrom
,
clientIp
);
return
pjp
.
proceed
();
}
JsonResult
result
=
JsonResult
.
buildSuccessResult
(
"图形验证码不正确"
,
""
);
result
.
setBusinessCode
(
"0002"
);
if
(
StringUtils
.
isNotBlank
(
captchaValue
))
{
// 忽略用户输入的大小写
String
captcha
=
StringUtils
.
lowerCase
(
captchaValue
);
// 验证码校验
Boolean
validCaptcha
=
false
;
try
{
validCaptcha
=
imageCaptchaService
.
validateResponseForID
(
Constants
.
IMAGE_CAPTCHA_KEY
+
captchaId
,
captcha
);
}
catch
(
CaptchaServiceException
ex
)
{
LOGGER
.
error
(
"验证码校验异常, {}, {}"
,
ex
.
getMessage
(),
ex
);
}
if
(
validCaptcha
)
{
return
pjp
.
proceed
();
}
return
result
;
}
LOGGER
.
info
(
"使用错误图形验证码, registerFrom={}, clientIp={},手机号次数:{},设备次数:{},ip次数:{},phone:{}"
,
registerFrom
,
clientIp
,
countPhone
,
countDeviceId
,
countIP
,
phoneNo
);
result
.
setMsg
(
"请输入图形验证码"
);
return
result
;
}
return
pjp
.
proceed
();
}
private
boolean
shouldSkipCaptchaValidate
(
String
registerFrom
,
String
captchaId
,
Object
captchaValue
)
{
// 如果启用了超级验证码功能, 检查超级验证码, 超级验证码区分大小写
if
(
autoTestCaptchaEnabled
)
{
return
true
;
}
return
StringUtils
.
equals
(
SUPER_CAPTCHA_ID
,
String
.
valueOf
(
captchaId
))
&&
StringUtils
.
equals
(
SUPER_CAPTCHA
,
String
.
valueOf
(
captchaValue
));
}
private
Long
countPhone
(
String
phoneNo
)
{
Long
count
=
1L
;
String
countString
=
redisTemplate
.
opsForValue
().
get
(
IMAGE_PHONE_COUNT
+
phoneNo
);
if
(
StringUtils
.
isBlank
(
countString
))
{
redisTemplate
.
opsForValue
().
set
(
IMAGE_PHONE_COUNT
+
phoneNo
,
String
.
valueOf
(
count
),
FIVE_MIN
,
TimeUnit
.
SECONDS
);
}
else
{
count
=
Long
.
valueOf
(
countString
)
+
1L
;
redisTemplate
.
opsForValue
().
set
(
IMAGE_PHONE_COUNT
+
phoneNo
,
String
.
valueOf
(
count
),
FIVE_MIN
,
TimeUnit
.
SECONDS
);
}
return
count
;
}
/**
* 短信发送限制
* @param clientId - 设备ID或IP
* @param device - true - 设备,false - IP
* @return
*/
private
Long
countByClientId
(
String
clientId
,
boolean
device
)
{
Long
count
=
1L
;
if
(
StringUtils
.
isBlank
(
clientId
))
{
return
count
;
}
else
{
String
key
=
(
device
?
IMAGE_DEVICEID_COUNT
:
IMAGE_IP_COUNT
)
+
clientId
;
String
countString
=
redisTemplate
.
opsForValue
().
get
(
key
);
if
(
StringUtils
.
isBlank
(
countString
))
{
redisTemplate
.
opsForValue
().
set
(
key
,
String
.
valueOf
(
count
),
FIVE_MIN
,
TimeUnit
.
SECONDS
);
}
else
{
count
=
Long
.
valueOf
(
countString
)
+
1L
;
redisTemplate
.
opsForValue
().
set
(
key
,
String
.
valueOf
(
count
),
FIVE_MIN
,
TimeUnit
.
SECONDS
);
}
return
count
;
}
}
}
src/main/java/cn/quantgroup/xyqb/aspect/captcha/CaptchaNewValidator.java
deleted
100644 → 0
View file @
865c856e
package
cn
.
quantgroup
.
xyqb
.
aspect
.
captcha
;
import
java.lang.annotation.*
;
/**
* Created by xuran on 2017/8/28.
*/
@Documented
@Target
(
ElementType
.
METHOD
)
@Retention
(
RetentionPolicy
.
RUNTIME
)
public
@interface
CaptchaNewValidator
{
}
src/main/java/cn/quantgroup/xyqb/aspect/captcha/CaptchaValidateAdvisor.java
View file @
c88d1ea1
package
cn
.
quantgroup
.
xyqb
.
aspect
.
captcha
;
import
cn.quantgroup.xyqb.Constants
;
import
cn.quantgroup.xyqb.model.JsonResult
;
import
cn.quantgroup.xyqb.thirdparty.jcaptcha.AbstractManageableImageCaptchaService
;
...
...
@@ -78,19 +77,16 @@ public class CaptchaValidateAdvisor {
HttpServletRequest
request
=
((
ServletRequestAttributes
)
RequestContextHolder
.
getRequestAttributes
()).
getRequest
();
String
registerFrom
=
Optional
.
ofNullable
(
request
.
getParameter
(
"registerFrom"
)).
orElse
(
""
);
String
captchaId
=
Optional
.
ofNullable
(
request
.
getParameter
(
"captchaId"
)).
orElse
(
""
);
Object
captchaValue
=
request
.
getParameter
(
"captchaValue"
);
String
captchaValue
=
request
.
getParameter
(
"captchaValue"
);
if
(
shouldSkipCaptchaValidate
(
registerFrom
,
captchaId
,
captchaValue
))
{
LOGGER
.
info
(
"使用超级图形验证码校验, registerFrom={}, clientIp={}"
,
registerFrom
,
IPUtil
.
getRemoteIP
(
request
));
return
pjp
.
proceed
();
}
JsonResult
result
=
JsonResult
.
buildSuccessResult
(
"图形验证码不正确"
,
""
);
result
.
setBusinessCode
(
"0002"
);
if
(
captchaValue
!=
null
)
{
String
captcha
=
String
.
valueOf
(
captchaValue
);
if
(
StringUtils
.
isNotBlank
(
captchaValue
))
{
// 忽略用户输入的大小写
captcha
=
StringUtils
.
lowerCase
(
captcha
);
String
captcha
=
StringUtils
.
lowerCase
(
captchaValue
);
// 验证码校验
Boolean
validCaptcha
=
false
;
try
{
...
...
@@ -98,7 +94,6 @@ public class CaptchaValidateAdvisor {
}
catch
(
CaptchaServiceException
ex
)
{
LOGGER
.
error
(
"验证码校验异常, {}, {}"
,
ex
.
getMessage
(),
ex
);
}
if
(
validCaptcha
)
{
return
pjp
.
proceed
();
}
...
...
src/main/java/cn/quantgroup/xyqb/aspect/limit/PasswordFreeAccessValidateAdvisor.java
View file @
c88d1ea1
...
...
@@ -13,9 +13,6 @@ 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.data.redis.core.RedisTemplate
;
import
org.springframework.stereotype.Component
;
import
org.springframework.web.context.request.RequestContextHolder
;
import
org.springframework.web.context.request.ServletRequestAttributes
;
...
...
@@ -38,10 +35,6 @@ public class PasswordFreeAccessValidateAdvisor {
private
static
final
String
PHONE_NO
=
"phoneNo"
;
private
static
final
String
USER_ID
=
"userId"
;
@Autowired
@Qualifier
(
"stringRedisTemplate"
)
private
RedisTemplate
<
String
,
String
>
redisTemplate
;
/**
* 免密访问校验切面
*/
...
...
src/main/java/cn/quantgroup/xyqb/controller/external/user/WeChatController.java
View file @
c88d1ea1
This diff is collapsed.
Click to expand it.
src/main/java/cn/quantgroup/xyqb/controller/internal/sms/SmsController.java
View file @
c88d1ea1
...
...
@@ -2,7 +2,6 @@ package cn.quantgroup.xyqb.controller.internal.sms;
import
cn.quantgroup.sms.MsgParams
;
import
cn.quantgroup.xyqb.Constants
;
import
cn.quantgroup.xyqb.aspect.captcha.CaptchaNewValidator
;
import
cn.quantgroup.xyqb.aspect.captcha.CaptchaValidator
;
import
cn.quantgroup.xyqb.controller.IBaseController
;
import
cn.quantgroup.xyqb.model.JsonResult
;
...
...
@@ -200,7 +199,7 @@ public class SmsController implements IBaseController {
/**
* 快速登陆发送验证码新版
*/
@Captcha
New
Validator
@CaptchaValidator
@RequestMapping
(
"/send_login_code_voice_new"
)
public
JsonResult
sendLoginCodeVoiceNew
(
@RequestParam
String
phoneNo
,
@RequestParam
(
required
=
false
)
String
registerFrom
,
String
usage
,
@RequestParam
(
required
=
false
)
String
deviceId
)
{
...
...
@@ -216,7 +215,7 @@ public class SmsController implements IBaseController {
/**
* 快速登陆发送短信验证码
*/
@Captcha
New
Validator
@CaptchaValidator
@RequestMapping
(
"/send_login_code_new"
)
public
JsonResult
sendLoginSmsCodeNew
(
@RequestParam
String
phoneNo
,
@RequestParam
(
required
=
false
)
String
registerFrom
,
@RequestParam
(
required
=
false
)
String
deviceId
,
@RequestParam
(
required
=
false
,
defaultValue
=
""
)
String
appName
)
{
LOGGER
.
info
(
"快速登陆-发送验证码, phoneNo:{}, registerFrom:{}"
,
phoneNo
,
registerFrom
);
...
...
@@ -225,7 +224,7 @@ public class SmsController implements IBaseController {
/**
* 快速登陆发送短信验证码
*/
@Captcha
New
Validator
@CaptchaValidator
@RequestMapping
(
"/send_login_code_new_forH5"
)
public
JsonResult
sendLoginSmsCodeNewForH5
(
@RequestParam
String
phoneNo
,
@RequestParam
(
required
=
false
)
String
registerFrom
,
@RequestParam
(
required
=
false
)
String
deviceId
,
@RequestParam
(
required
=
false
,
defaultValue
=
""
)
String
appName
)
{
LOGGER
.
info
(
"快速登陆-发送验证码, phoneNo:{}, registerFrom:{}"
,
phoneNo
,
registerFrom
);
...
...
src/main/java/cn/quantgroup/xyqb/controller/internal/user/UserDetailController.java
View file @
c88d1ea1
src/main/java/cn/quantgroup/xyqb/service/auth/impl/UserAuthorizedServiceImpl.java
View file @
c88d1ea1
...
...
@@ -9,11 +9,13 @@ import cn.quantgroup.xyqb.service.auth.IUserAuthorizedService;
import
org.apache.commons.lang.StringUtils
;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
import
org.springframework.cache.annotation.Cacheable
;
import
org.springframework.stereotype.Service
;
import
javax.annotation.Resource
;
import
java.sql.Timestamp
;
import
java.text.ParseException
;
import
java.util.Objects
;
/**
* @author xufei on 2018/1/5.
...
...
@@ -27,6 +29,7 @@ public class UserAuthorizedServiceImpl implements IUserAuthorizedService {
@Resource
private
IUserAuthorizedRepository
userAuthorizedRepository
;
@Cacheable
(
value
=
"user_authorized_idno_cache"
,
key
=
"#idNo"
,
unless
=
"#result == null"
,
cacheManager
=
"cacheManager"
)
@Override
public
Boolean
hasUserAuthorized
(
String
idNo
)
{
try
{
...
...
@@ -35,6 +38,7 @@ public class UserAuthorizedServiceImpl implements IUserAuthorizedService {
}
}
catch
(
ParseException
e
)
{
LOGGER
.
error
(
"[hasUserAuthorized]参数异常e:{}"
,
e
);
return
Boolean
.
FALSE
;
}
UserAuthorized
userAuthorized
=
userAuthorizedRepository
.
findByIdNo
(
idNo
);
...
...
@@ -62,6 +66,10 @@ public class UserAuthorizedServiceImpl implements IUserAuthorizedService {
@Override
public
UserAuthorized
createUserAuthorized
(
UserAuthorizedParam
userAuthorizedParam
)
{
// 数据检查
if
(
Objects
.
isNull
(
userAuthorizedParam
)
||
this
.
hasUserAuthorized
(
userAuthorizedParam
.
getIdNo
())){
return
null
;
}
AuthPattern
authPatternEnum
=
AuthPattern
.
valueOf
(
userAuthorizedParam
.
getAuthPattern
());
UserAuthorized
userAuthorized
=
new
UserAuthorized
();
...
...
@@ -84,6 +92,7 @@ public class UserAuthorizedServiceImpl implements IUserAuthorizedService {
}
}
@Cacheable
(
value
=
"user_authorized_uuid_2_id_cache"
,
key
=
"#userUuid"
,
unless
=
"#result == null"
,
cacheManager
=
"cacheManager"
)
@Override
public
String
getUserAuthorizedId
(
String
userUuid
)
{
if
(
StringUtils
.
isBlank
(
userUuid
))
{
...
...
src/main/java/cn/quantgroup/xyqb/util/IPUtil.java
View file @
c88d1ea1
...
...
@@ -32,6 +32,7 @@ public class IPUtil {
String
jvmTest
=
System
.
getProperty
(
"test"
);
if
(
Boolean
.
valueOf
(
jvmTest
)){
WHITE_ADDRESS
.
add
(
"192.168."
);
WHITE_ADDRESS
.
add
(
"10."
);
WHITE_ADDRESS
.
add
(
LOCAL_ADDRESS
);
}
}
...
...
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