Commit d3c4e1e9 authored by 技术部-任文超's avatar 技术部-任文超

Merge branch 'master' into 20180424-modify-UserDetail

parents 5b69b871 87deaeb0
......@@ -29,6 +29,23 @@
</properties>
<dependencies>
<!-- swagger2 start -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>RELEASE</version>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-core</artifactId>
<version>RELEASE</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>RELEASE</version>
</dependency>
<!-- swagger2 end -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
......
package cn.quantgroup.xyqb.config.swagger;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.google.common.base.Predicates;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
/**
* Swagger文档框架
*/
@EnableSwagger2
@Configuration
public class SwaggerConfig {
@Value("${openapi.swagger.on:false}")
private Boolean swaggerOn;
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.useDefaultResponseMessages(false)
.enable(swaggerOn)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
.paths(PathSelectors.any())
.paths(Predicates.not(PathSelectors.regex("/error.*")))
.build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("xyqb-user api")
.description("用户中心接口文档")
.contact(new Contact("wenchao.ren", "", "wenchao.ren@quantgroup.cn"))
.license("Apache License Version 2.0")
.licenseUrl("https://github.com/springfox/springfox/blob/master/LICENSE")
.version("2.0")
.build();
}
@Component
@Primary
public class CustomObjectMapper extends ObjectMapper {
public CustomObjectMapper() {
setSerializationInclusion(JsonInclude.Include.NON_NULL);
configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
enable(SerializationFeature.INDENT_OUTPUT);
}
}
}
......@@ -393,7 +393,7 @@ public class InnerController implements IBaseController {
*/
@LogHttpCaller
@RequestMapping("/user_detail/update")
public JsonResult updateUserQQ(String qq, String email, Long userId) {
public JsonResult updateUserDetail(String qq, String email, Long userId) {
if (Objects.isNull(userId) || userId == 0L) {
return JsonResult.buildErrorStateResult("userId为空", null);
}
......
package cn.quantgroup.xyqb.controller.external.user;
import cn.quantgroup.xyqb.Constants;
import cn.quantgroup.xyqb.aspect.accessable.IpValidator;
import cn.quantgroup.xyqb.aspect.logcaller.LogHttpCaller;
import cn.quantgroup.xyqb.entity.User;
import cn.quantgroup.xyqb.model.JsonResult;
import cn.quantgroup.xyqb.model.session.SessionStruct;
import cn.quantgroup.xyqb.service.api.IUserApiService;
import cn.quantgroup.xyqb.service.session.ISessionService;
import cn.quantgroup.xyqb.service.user.IUserService;
import cn.quantgroup.xyqb.session.XyqbSessionContextHolder;
import cn.quantgroup.xyqb.util.ValidationUtil;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.Objects;
/**
* Created by FrankChow on 15/12/16.
*/
@Slf4j
@RestController
@RequestMapping("/api")
public class UserApiController {
private static final Logger LOGGER = LoggerFactory.getLogger(UserApiController.class);
@Autowired
private IUserApiService userApiService;
@Autowired
@Resource
private IUserService userService;
@Resource
private IUserApiService userApiService;
@Resource
private ISessionService sessionService;
@Resource
@Qualifier("stringRedisTemplate")
private RedisTemplate<String, String> stringRedisTemplate;
@RequestMapping("/user/check")
public JsonResult userImportCheck(String phoneNo, String registerFrom) {
if ("244".equals(registerFrom)) {
LOGGER.info("[user_import_check]用户导入检查拒绝。phoneNo=[{}], registerFrom=[{}]", phoneNo, registerFrom);
log.info("[user_import_check]用户导入检查拒绝。phoneNo=[{}], registerFrom=[{}]", phoneNo, registerFrom);
return JsonResult.buildErrorStateResult("用户导入检查拒绝", false);
}
if (StringUtils.isEmpty(phoneNo) || StringUtils.isEmpty(registerFrom)) {
LOGGER.error("[user_import_check]检查传入的参数,参数不全。phoneNo=[{}], registerFrom=[{}]", phoneNo, registerFrom);
log.error("[user_import_check]检查传入的参数,参数不全。phoneNo=[{}], registerFrom=[{}]", phoneNo, registerFrom);
return JsonResult.buildErrorStateResult("检查传入的参数,参数不全。", null);
}
boolean checkPassed = userApiService.userImportCheck(phoneNo);
if (checkPassed) {
LOGGER.info("[user_import_check]用户可以导入。phoneNo=[{}], registerFrom=[{}]", phoneNo, registerFrom);
log.info("[user_import_check]用户可以导入。phoneNo=[{}], registerFrom=[{}]", phoneNo, registerFrom);
return JsonResult.buildSuccessResult("用户可以导入", checkPassed);
}
LOGGER.info("[user_import_check]用户导入检查拒绝。phoneNo=[{}], registerFrom=[{}]", phoneNo, registerFrom);
log.info("[user_import_check]用户导入检查拒绝。phoneNo=[{}], registerFrom=[{}]", phoneNo, registerFrom);
return JsonResult.buildErrorStateResult("用户导入检查拒绝", checkPassed);
}
......@@ -59,4 +74,44 @@ public class UserApiController {
return JsonResult.buildErrorStateResult(null, null, 2L);
}
/**
* 检查token是否有效
* 如果有效,可选择是否延续生命期(延续后有效期24Hour)
*
* @param token - sid,session的id
* @param prolong - 是否延续生命期,可选参数,默认为: false - 不延续
* @return
*/
@ApiOperation(notes = "检查token是否有效,如果有效,可选择是否延续生命期(延续后有效期24Hour)", value = "Check token and then prolong session", nickname = "checkToken")
@LogHttpCaller
@IpValidator
@RequestMapping(value = "/valid/{token}", method = RequestMethod.POST)
public JsonResult checkToken(@ApiParam(value = "sid,session的id", required = true) @PathVariable("token") String token,
@ApiParam(value = "是否延续生命期,可选参数,默认为: false - 不延续", required = false) @RequestParam(name = "prolong", required = false, defaultValue = "false") Boolean prolong) {
if(Objects.isNull(token) || !ValidationUtil.validateToken(token)){
return JsonResult.buildErrorStateResult("token invalid", token);
}
String tokenKey = Constants.SESSION_PREFIX + token;
String tokenKey2 = Constants.Session.USER_SESSION_CACHE + token;
// 判断token是否存在
boolean exist = stringRedisTemplate.hasKey(tokenKey)||stringRedisTemplate.hasKey(tokenKey2);
/* token存在且需要延续时,进一步判断session是否有效,有效时,自动续期 */
if(Boolean.logicalAnd(exist, prolong)){
// 获取session信息
SessionStruct sessionStruct = XyqbSessionContextHolder.getXSessionFromRedis(token);
if(Objects.isNull(sessionStruct)) {
/* 如果没有获取到session信息则返回错误信息 */
return JsonResult.buildErrorStateResult("session invalid", token);
}else{
/* 延续session生命期 */
try {
sessionService.persistSession(sessionStruct.getSid(), sessionStruct.getValues());
} finally {
XyqbSessionContextHolder.releaseSession();
}
}
}
return JsonResult.buildSuccessResult("token valid", token);
}
}
package cn.quantgroup.xyqb.controller.internal.login;
import cn.quantgroup.xyqb.aspect.logcaller.LogHttpCaller;
import cn.quantgroup.xyqb.controller.IBaseController;
import cn.quantgroup.xyqb.model.JsonResult;
import cn.quantgroup.xyqb.model.UserRet;
......@@ -15,6 +16,8 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
/**
* 查询已登录信息
* Created by Miraculous on 2016/12/30.
......@@ -25,8 +28,9 @@ import org.springframework.web.bind.annotation.RestController;
public class AuthInfoController implements IBaseController {
@LogHttpCaller
@RequestMapping("/info/login")
public JsonResult loginInfo() {
public JsonResult loginInfo(HttpServletRequest request) {
SessionStruct sessionStruct = getCurrentSessionFromRedis();
if(null != sessionStruct) {
log.info("从用户中心获取到了用户登录信息:phone:[{}]", sessionStruct.getValues().getUser().getPhoneNo());
......@@ -40,10 +44,10 @@ public class AuthInfoController implements IBaseController {
context.setBtMerchantId(sessionStruct.getValues().getLoginProperties().getBtMerchantId());
//有ThreadLocal不释放的问题,不可再使用原来方式了
loginInfo.setLoginContext(context);
log.info("[/auth/info/login] SessionStruct数据:{}", JSONObject.toJSONString(sessionStruct));
log.info("[/auth/info/login] LoginInfo数据:{}", JSONObject.toJSONString(loginInfo));
log.info("[/auth/info/login] SessionStruct数据:{}, LoginInfo数据:{}", JSONObject.toJSONString(sessionStruct), JSONObject.toJSONString(loginInfo));
return JsonResult.buildSuccessResult("", loginInfo);
}
log.info("[/auth/info/login] 未查到用户登录信息, request-Header:{}", JSON.toJSONString(getRequestHeaderMap(request)));
return JsonResult.buildErrorStateResult("用户未登录",null);
}
......
......@@ -28,9 +28,6 @@ import org.springframework.web.bind.annotation.RestController;
@RequestMapping("/platform/api")
public class PlatformAPIController implements IBaseController {
private static final Logger LOGGER = LoggerFactory.getLogger(PlatformAPIController.class);
@Autowired
private IPageService pageService;
@Autowired
......@@ -81,6 +78,4 @@ public class PlatformAPIController implements IBaseController {
return JsonResult.buildSuccessResult("", ImmutableMap.of("type", "user", "transition", nextPage));
}
}
......@@ -17,6 +17,7 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Objects;
/**
* Created by 11 on 2016/12/29.
......@@ -24,10 +25,13 @@ import java.io.PrintWriter;
@Component
public class RequestFilter implements Filter {
private static final String[] ALLOWED_PATTERNS = {
"/wechat/**", "/config/**", "/api/**", "/query/**", "/user_detail/**", "/hello/**", "/innerapi/**", "/app/**", "/motan/**", "/user/**", "/lock/**",
"/auth/info/login", "/platform/api/page/return_url", "/MP_verify_AWiagUn4kZiwmTt0.txt", "/tech/health/check"
};
/**
* 带状态
*/
private static final String[] ALLOWED_PATTERNS = {"/auth/info/login", "/platform/api/page/next"};
/**
* 带状态请求鉴权失败时的响应信息
*/
private static final String UNAUTH_RESULT = JSONObject.toJSONString(JsonResult.buildErrorStateResult("登录失败", null));
@Autowired
private ISessionService sessionService;
......@@ -41,38 +45,37 @@ public class RequestFilter implements Filter {
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
String requestPath = getRequestPath(request);
SessionStruct sessionStruct;
if (!isMatch(requestPath)) {
//获取session信息,如果没有获取到session信息则返回错误信息
sessionStruct = XyqbSessionContextHolder.getXSessionFromRedis();
if (sessionStruct == null) {
// 带状态接口
if(isMatch(request)){
// 需获取session信息
SessionStruct sessionStruct = XyqbSessionContextHolder.getXSessionFromRedis();
if(Objects.isNull(sessionStruct)) {
/* 如果没有获取到session信息则返回错误信息 */
response.setStatus(401);
response.setHeader("Content-Type", "application/json;charset=utf-8");
PrintWriter writer = response.getWriter();
writer.print(UNAUTH_RESULT);
writer.close();
return;
}
try {
filterChain.doFilter(request, response);
} finally {
sessionStruct = XyqbSessionContextHolder.getXSessionFromRedis();
if (sessionStruct != null) {
try {
sessionService.persistSession(sessionStruct.getSid(), sessionStruct.getValues());
} finally {
XyqbSessionContextHolder.releaseSession();
}
}else{
/* 延续session生命期 */
try {
sessionService.persistSession(sessionStruct.getSid(), sessionStruct.getValues());
} finally {
XyqbSessionContextHolder.releaseSession();
}
}
} else {
filterChain.doFilter(request, response);
}
filterChain.doFilter(request, response);
}
private boolean isMatch(String path) {
/**
* 判断是否带状态请求
* @param request
* @return
*/
private boolean isMatch(HttpServletRequest request) {
String path = getRequestPath(request);
for (String pattern : ALLOWED_PATTERNS) {
if (matcher.match(pattern, path)) {
return true;
......@@ -83,8 +86,7 @@ public class RequestFilter implements Filter {
private String getRequestPath(HttpServletRequest request) {
String url = request.getServletPath();
if (request.getPathInfo() != null) {
if (Objects.nonNull(request.getPathInfo())) {
url += request.getPathInfo();
}
return url;
......
......@@ -152,7 +152,7 @@ public class SessionServiceImpl implements ISessionService {
Constants.Session.ONE_DAY, TimeUnit.SECONDS);
String key = generateLoginPropertiesKey(sessionValue.getUser().getId(), sessionValue.getLoginProperties());
stringRedisTemplate.opsForValue().set(key, token, Constants.Session.ONE_DAY, TimeUnit.SECONDS);
log.info("[Session生命期延续],token:{},有效期:[24Hour]", token);
setUserIdTokenKeys(sessionValue.getUser().getId(), key);
}
......@@ -181,18 +181,21 @@ public class SessionServiceImpl implements ISessionService {
public SessionStruct findSessionBySessionId(String sessionId) {
String sessionValue = findSessionValueBySessionId(sessionId);
if (StringUtils.isEmpty(sessionValue)) {
log.warn("[SessionServiceImpl][findSessionBySessionId] session data 未找到:sid:{}", sessionId);
return null;
}
try {
SessionValue value = JSON.parseObject(sessionValue, SessionValue.class);
if (null == value) {
log.warn("[SessionServiceImpl][findSessionBySessionId] session data 未找到:sid:{},sessionValue:{}", sessionId, sessionValue);
return null;
}
SessionStruct struct = new SessionStruct();
struct.setSid(sessionId);
struct.setValues(value);
return struct;
} catch (Exception ex) {
} catch (Exception e) {
log.warn("[SessionServiceImpl][findSessionBySessionId] 序列化SessionValue出错:sid:{},sessionValue:{}", sessionId, sessionValue, e);
return null;
}
......
......@@ -4,6 +4,7 @@ import cn.quantgroup.xyqb.Constants;
import cn.quantgroup.xyqb.model.session.SessionStruct;
import cn.quantgroup.xyqb.model.session.SessionValue;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -17,10 +18,10 @@ import java.util.Objects;
/**
* Created by Miraculous on 2016/12/29.
*/
@Slf4j
public class XyqbSessionContextHolder {
private static final ThreadLocal<SessionStruct> threadSession = new ThreadLocal<>();
private static final Logger LOGGER = LoggerFactory.getLogger(XyqbSessionContextHolder.class);
public static RedisTemplate<String, String> redisTemplate = null;
public static SessionStruct getXSession() {
......@@ -46,20 +47,21 @@ public class XyqbSessionContextHolder {
}
String result = redisTemplate.opsForValue().get(Constants.Session.USER_SESSION_CACHE + token);
if (StringUtils.isEmpty(result)) {
log.warn("[XyqbSessionContextHolder][getXSessionFromRedis] session data 未找到:sid:{},sessionValue:{}", token, result);
return null;
}
try {
SessionValue values = JSON.parseObject(result, SessionValue.class);
if (values == null) {
log.warn("[XyqbSessionContextHolder][getXSessionFromRedis] session data 未找到:sid:{},sessionValue:{}", token, result);
return null;
}
SessionStruct sessionStruct = new SessionStruct();
sessionStruct.setSid(token);
sessionStruct.setValues(values);
return sessionStruct;
}catch (Exception ex){
LOGGER.error("序列化session出错", ex);
}catch (Exception e){
log.warn("[XyqbSessionContextHolder][getXSessionFromRedis] 序列化SessionValue出错:sid:{},sessionValue:{}", token, result, e);
return null;
}
}
......
......@@ -23,11 +23,12 @@ public class IPUtil {
* 172.16.0.0/16 - 公有云正式业务
* 172.20.0.0/16 - 3B私有云
* 172.30.0.0/16 - 3C私有云
* 172.41.0.0/16 - 3B,docker内网
*/
private static final Set<String> WHITE_ADDRESS = Sets.newHashSet();
private static final String LOCAL_ADDRESS = "127.0.0.1";
static {
String[] ips = {"172.16.", "172.20.", "172.30.", "192.168.3.", "192.168.4."};
String[] ips = {"172.16.", "172.20.", "172.30.", "172.41.", "192.168.3.", "192.168.4."};
WHITE_ADDRESS.addAll(Arrays.asList(ips));
//系统环境
if(!TechEnvironment.isPro()){
......
......@@ -22,13 +22,20 @@ public class ValidationUtil {
private static final String chineseNameExtendRegExp = "^[\u4dae\u4e00-\u9fff]+(\\.|·)?[\u4dae\u4e00-\u9fff]+$";
private static final String ipv4RegExp = "^((2[0-4][0-9]|25[0-5]|[01]?[0-9][0-9]?)\\.){3}(2[0-4][0-9]|25[0-5]|[01]?[0-9][0-9]?)$";
private static final String localIpv4RegExp = "^((172\\.(1[0-6]|2[0-9]|3[01]))|(192\\.168|169\\.254)|((127|10)\\.(2[0-4][0-9]|25[0-5]|[01]?[0-9][0-9]?)))(\\.(2[0-4][0-9]|25[0-5]|[01]?[0-9][0-9]?)){2}$";
private static final String tokenRegExp = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$";
private static final Pattern phonePattern = Pattern.compile(phoneRegExp);
private static final Pattern chinesePattern = Pattern.compile(chineseNameRegExp);
private static final Pattern chineseExtendPattern = Pattern.compile(chineseNameExtendRegExp);
private static final Pattern ipv4Pattern = Pattern.compile(ipv4RegExp);
private static final Pattern localIpv4Pattern = Pattern.compile(localIpv4RegExp);
private static final Pattern tokenPattern = Pattern.compile(tokenRegExp);
/**
* 是否是合法的中国大陆手机号
* @param phoneNo
* @return
*/
public static boolean validatePhoneNo(String phoneNo) {
boolean lengthValid = StringUtils.isNotBlank(phoneNo) && phoneNo.length() == 11 && StringUtils.isNumeric(phoneNo);
if (!lengthValid) {
......@@ -38,6 +45,11 @@ public class ValidationUtil {
return matcher.find();
}
/**
* 是否是合法的中文姓名
* @param chinese
* @return
*/
public static boolean validateChinese(String chinese) {
if (StringUtils.isBlank(chinese)) {
return false;
......@@ -81,6 +93,19 @@ public class ValidationUtil {
return matcher.find();
}
/**
* 是否是合法的用户中心token
* @param token
* @return
*/
public static boolean validateToken(String token) {
if (StringUtils.isBlank(token)) {
return false;
}
Matcher matcher = tokenPattern.matcher(token);
return matcher.find();
}
/**
* 验证密令
* 私钥 + 操作 + 时
......
......@@ -6,8 +6,8 @@ import java.util.Base64;
public class TestStringCode {
public static void main(String[] args) {
System.out.println(ap_base64("13511112222", "000000"));
System.out.println(pc_base64("15566660006", "0000"));
System.out.println(ap_base64("18510236666", "123456"));
System.out.println(pc_base64("18222288391", "0000"));
}
final static String AUTHORIZATION = "authorization";
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment