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

更新One-Time Token获取策略,

优化UserController代码,
重新梳理用户安全问题解决方案的应对方法,
恢复旧接口的行为到分支初始状态
parent 072528aa
...@@ -12,7 +12,7 @@ public interface Constants { ...@@ -12,7 +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_ONCE_KEY_FOR_PHONE = "token_once:for_phone:"; String ONE_TIME_TOKEN = "One-Time-Token";
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 + "*";
......
package cn.quantgroup.xyqb.aspect.token;
import cn.quantgroup.xyqb.Constants;
import cn.quantgroup.xyqb.model.JsonResult;
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.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.util.Objects;
/**
* 一次性令牌校验切面
*
* @author 任文超
* @version 1.0.0
* @since 2017-10-31
*/
@Aspect
@Component
public class OneTimeTokenValidateAdvisor {
private static final Logger LOGGER = LoggerFactory.getLogger(OneTimeTokenValidateAdvisor.class);
@Autowired
@Qualifier("stringRedisTemplate")
private RedisTemplate<String, String> redisTemplate;
/**
* 一次性令牌校验切面
*/
@Pointcut("@annotation(cn.quantgroup.xyqb.aspect.token.OneTimeTokenValidator)")
private void needOneTimeToken() {}
/**
* 在受一次性令牌保护的接口方法执行前, 执行一次性令牌校验
*
* @throws Throwable
*/
@Around("needOneTimeToken()")
private Object checkOneTimeToken(ProceedingJoinPoint pjp) throws Throwable {
boolean valid = oneTimeTokenValid();
if (valid) {
return pjp.proceed();
}
return JsonResult.buildSuccessResult("Token过期,请重新请求", "", 2L);
}
/**
* 校验一次性令牌
* @return True or False
*/
private boolean oneTimeTokenValid() {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
// 当前请求的OneTimeToken
String oneTimeToken = request.getHeader(Constants.ONE_TIME_TOKEN);
if (StringUtils.isBlank(oneTimeToken)){
return false;
}
String oneTimeToken_value = redisTemplate.opsForValue().get(oneTimeToken);
// OneTimeToken再Redis中值不应为空值(空白、空格、null)
if (StringUtils.isBlank(oneTimeToken_value)) {
return false;
}
boolean valid = Objects.equals(Boolean.TRUE.toString(), oneTimeToken_value);
// OneTimeToken校验正确时删除key
if(valid) {
redisTemplate.delete(oneTimeToken);
}else {
LOGGER.info("Token过期,请重新请求, One-Time-Token={}, clientIp={}", oneTimeToken, request.getRemoteAddr());
}
return valid;
}
}
...@@ -3,7 +3,7 @@ package cn.quantgroup.xyqb.aspect.token; ...@@ -3,7 +3,7 @@ package cn.quantgroup.xyqb.aspect.token;
import java.lang.annotation.*; import java.lang.annotation.*;
/** /**
* 需要单次令牌校验标记 * 一次性令牌校验标记
* @author 任文超 * @author 任文超
* @version 1.0.0 * @version 1.0.0
* @since 2017-10-31 * @since 2017-10-31
...@@ -11,5 +11,5 @@ import java.lang.annotation.*; ...@@ -11,5 +11,5 @@ import java.lang.annotation.*;
@Documented @Documented
@Target(ElementType.METHOD) @Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
public @interface TokenOnceValidator { public @interface OneTimeTokenValidator {
} }
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.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.nio.charset.Charset;
import java.util.Base64;
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 TokenOnceValidateAdvisor {
private static final Logger LOGGER = LoggerFactory.getLogger(TokenOnceValidateAdvisor.class);
@Autowired
@Qualifier("stringRedisTemplate")
private RedisTemplate<String, String> redisTemplate;
/**
* 单次令牌校验切面
*/
@Pointcut("@annotation(cn.quantgroup.xyqb.aspect.token.TokenOnceValidator)")
private void needTokenOnceValidate() {
}
/**
* 在受单次令牌保护的接口方法执行前, 执行单次令牌校验
*
* @throws Throwable
*/
@Around("needTokenOnceValidate()")
private Object doTokenOnceValidate(ProceedingJoinPoint pjp) throws Throwable {
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");
// 当前请求的TokenOnce
String requestToken = phoneTokenMap.get("requestToken");
if (StringUtils.isBlank(phoneNo) || StringUtils.isBlank(requestToken)){
return false;
}
final String key = Constants.TOKEN_ONCE_KEY_FOR_PHONE + phoneNo;
String tokenOnce = redisTemplate.opsForValue().get(key);
// TokenOnce不应为空值(空白、空格、null)
if (StringUtils.isBlank(tokenOnce)) {
redisTemplate.delete(key);
return false;
}
boolean valid = Objects.equals(tokenOnce, requestToken);
// TokenOnce校验正确时删除key
if(valid) {
redisTemplate.delete(key);
}else {
LOGGER.info("Token过期,请重新请求, token_once:for_phone:={}, requestToken={}, clientIp={}", phoneNo, requestToken, request.getRemoteAddr());
}
return valid;
}
/**
* 单次令牌参数解析
*
* @param request 当前请求,其首部行必须包含形如【TokenOnce MTM0NjEwNjc2NjI6NmFjMDY2NWItZTE5Yy00MzkyLWEyNDQtN2I2MTY5MDgzM2Y1】的UTF-8编码的Base64加密参数
* @return 令牌参数Map 或 null
*/
private Map<String, String> getHeaderParam(HttpServletRequest request) {
String headerName = "TokenOnce";
String credential = request.getHeader(headerName);
if (StringUtils.isBlank(credential)) {
LOGGER.info("令牌参数无效, credential:{}", credential);
return null;
}
boolean headerParamValid = true;
byte[] buf = Base64.getDecoder().decode(credential.getBytes());
credential = new String(buf, Charset.forName("UTF-8"));
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];
// 当前请求的TokenOnce
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;
}
}
package cn.quantgroup.xyqb.controller.internal.token; package cn.quantgroup.xyqb.controller.internal.token;
import cn.quantgroup.xyqb.Constants;
import cn.quantgroup.xyqb.controller.IBaseController; import cn.quantgroup.xyqb.controller.IBaseController;
import cn.quantgroup.xyqb.model.JsonResult; import cn.quantgroup.xyqb.model.JsonResult;
import org.apache.commons.codec.binary.Base64;
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.Autowired;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate; 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.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.nio.charset.Charset;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
/** /**
* 发放单次令牌 * 发放一次性令牌
* *
* @author 任文超 * @author 任文超
* @version 1.0.0 * @version 1.0.0
...@@ -28,32 +20,25 @@ import java.util.concurrent.TimeUnit; ...@@ -28,32 +20,25 @@ import java.util.concurrent.TimeUnit;
*/ */
@RestController @RestController
@RequestMapping("/token") @RequestMapping("/token")
public class TokenOnceController implements IBaseController { public class OneTimeTokenController implements IBaseController {
private static final Logger LOGGER = LoggerFactory.getLogger(TokenOnceController.class); private static final Long SECONDS = 1 * 60L;
private static final Long ONE_HOUR = 10 * 60L;
@Autowired @Autowired
@Qualifier("stringRedisTemplate") @Qualifier("stringRedisTemplate")
private RedisTemplate<String, String> redisTemplate; private RedisTemplate<String, String> redisTemplate;
/** /**
* 向指定用户账号(手机号)发放一枚TokenOnce * 发放一枚一次性令牌
* TokenOnce用法:其首部行必须包含形如【TokenOnce MTM0NjEwNjc2NjI6NmFjMDY2NWItZTE5Yy00MzkyLWEyNDQtN2I2MTY5MDgzM2Y1】的UTF-8编码的Base64加密参数 * One-Time Token用法:http请求首部行添加参数{One-Time-Token 6ac0665b-e19c-4392-a244-7b61690833f5}
* 例如:Base64.getEncoder().encodeToString("13461067662:6ac0665b-e19c-4392-a244-7b61690833f5".getBytes(Charset.forName("UTF-8")));
* *
* @param phoneNo 用户账号(手机号) * @return 一次性令牌
* @return 单次令牌
*/ */
@RequestMapping(value = "/once") @RequestMapping(value = "/oneTime")
public JsonResult newTokenOnce(HttpServletRequest request, @ModelAttribute("phoneNo") String phoneNo) { public JsonResult oneTimeToken() {
if (StringUtils.isBlank(phoneNo)){ String token = UUID.randomUUID().toString();
return JsonResult.buildErrorStateResult("获取TokenOnce失败", ""); redisTemplate.opsForValue().set(token, Boolean.TRUE.toString(), SECONDS, TimeUnit.SECONDS);
} return JsonResult.buildSuccessResult("", token);
String tokenOnce = UUID.randomUUID().toString();
final String key = Constants.TOKEN_ONCE_KEY_FOR_PHONE + phoneNo;
redisTemplate.opsForValue().set(key, tokenOnce, ONE_HOUR, TimeUnit.SECONDS);
return JsonResult.buildSuccessResult("", tokenOnce);
} }
} }
...@@ -27,7 +27,7 @@ public class RequestFilter implements Filter { ...@@ -27,7 +27,7 @@ public class RequestFilter implements Filter {
private static final String[] ALLOWED_PATTERNS = { private static final String[] ALLOWED_PATTERNS = {
"/user_detail/**","/hello/**","/innerapi/**", "/user/exist", "/motan/**", "/user/register", "/user/login", "/user/register/fast", "/user_detail/**","/hello/**","/innerapi/**", "/user/exist", "/motan/**", "/user/register", "/user/login", "/user/register/fast",
"/token/once", "/user/loginForH5", "/user/register/fastForH5", "/token/oneTime", "/user/loginForH5", "/user/register/fastForH5",
"/auth/info/login","/user/login/fast","/user/reset_password", "/user/exist_check","/user/center/**", "/auth/info/login","/user/login/fast","/user/reset_password", "/user/exist_check","/user/center/**",
"/jr58/**", "/app/login", "/app/login_super","/app/login2","/user/login2", "/wechat/**", "/config/**", "/api/**", "/user/exists_token","/query/**", "/jr58/**", "/app/login", "/app/login_super","/app/login2","/user/login2", "/wechat/**", "/config/**", "/api/**", "/user/exists_token","/query/**",
"/platform/api/page/return_url", "/MP_" + "/platform/api/page/return_url", "/MP_" +
......
package login; package login;
import cn.quantgroup.xyqb.Bootstrap; import cn.quantgroup.xyqb.Bootstrap;
import cn.quantgroup.xyqb.Constants;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.octo.captcha.service.CaptchaServiceException;
import org.apache.commons.lang3.StringUtils;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
...@@ -21,13 +24,14 @@ import org.springframework.web.context.WebApplicationContext; ...@@ -21,13 +24,14 @@ import org.springframework.web.context.WebApplicationContext;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.Base64; import java.util.Base64;
import java.util.Optional;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@RunWith(SpringJUnit4ClassRunner.class) @RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Bootstrap.class) @SpringApplicationConfiguration(classes = Bootstrap.class)
@WebAppConfiguration @WebAppConfiguration
public class LoginWithTokenOnceTests { public class LoginForH5Tests {
final String phoneNo = "13461067662"; final String phoneNo = "13461067662";
final String userName = "root"; final String userName = "root";
final String password = "!QAZ2wsx"; final String password = "!QAZ2wsx";
...@@ -54,71 +58,19 @@ public class LoginWithTokenOnceTests { ...@@ -54,71 +58,19 @@ public class LoginWithTokenOnceTests {
.andExpect(status().isOk()); .andExpect(status().isOk());
mvc.perform(MockMvcRequestBuilders.get("/user/login/fastForH5").accept(MediaType.APPLICATION_JSON)) mvc.perform(MockMvcRequestBuilders.get("/user/login/fastForH5").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk()); .andExpect(status().isOk());
mvc.perform(MockMvcRequestBuilders.get("/api/captcha").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
mvc.perform(MockMvcRequestBuilders.get("/api/sms/send_login_code").accept(MediaType.APPLICATION_JSON)) mvc.perform(MockMvcRequestBuilders.get("/api/sms/send_login_code").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk()); .andExpect(status().isOk());
} }
/** /**
* 测试TokenOnce发放服务 * 测试图形验证码账密登录
* @throws Exception * @throws Exception
*/ */
@Test @Test
public void testLoginFoH5() throws Exception{ public void testLoginFoH5() throws Exception{
// 获取TokenOnce Assert.assertTrue(false);
String tokenOnceUri = "/token/once";
MvcResult mvcResult = mvc.perform(MockMvcRequestBuilders.get(tokenOnceUri).accept(MediaType.APPLICATION_JSON)
.param("phoneNo", phoneNo))
.andExpect(status().isOk())
.andReturn();
String content = mvcResult.getResponse().getContentAsString();
JSONObject jsonResult = JSON.parseObject(new String(content));
Object code = jsonResult.get("code");
Assert.assertEquals("0000", code);
Object data = jsonResult.get("data");
Assert.assertNotNull(data);
StringBuilder tokenBuilder = new StringBuilder(phoneNo);
String tokenOnce = new String(Base64.getEncoder().encodeToString(tokenBuilder.append(":").append(data).toString().getBytes(Charset.forName("UTF-8"))));
StringBuilder basicBuilder = new StringBuilder();
String authorization = "Basic " + new String(Base64.getEncoder().encodeToString(basicBuilder.append(userName).append(":").append(password).toString().getBytes(Charset.forName("UTF-8"))));
// 第一次使用TokenOnce
String aspectUri = "/user/loginForH5";
mvcResult = mvc.perform(MockMvcRequestBuilders.get(aspectUri).accept(MediaType.APPLICATION_JSON)
.header("TokenOnce", tokenOnce)
.header("authorization", authorization)
.param("openId", "none"))
.andExpect(status().isOk())
.andReturn();
content = mvcResult.getResponse().getContentAsString();
jsonResult = JSON.parseObject(new String(content));
code = jsonResult.get("code");
Object businessCode = jsonResult.get("businessCode");
Assert.assertEquals("0000", code);
Assert.assertNotEquals("0002", businessCode);
// 使用过期的TokenOnce
mvcResult = mvc.perform(MockMvcRequestBuilders.get(aspectUri).accept(MediaType.APPLICATION_JSON)
.header("TokenOnce", tokenOnce)
.header("authorization", authorization)
.param("openId", "none"))
.andExpect(status().isOk())
.andReturn();
content = mvcResult.getResponse().getContentAsString();
jsonResult = JSON.parseObject(new String(content));
code = jsonResult.get("code");
Assert.assertEquals("0000", code);
businessCode = jsonResult.get("businessCode");
Assert.assertEquals("0002", businessCode);
// 不使用TokenOnce
mvcResult = mvc.perform(MockMvcRequestBuilders.get(aspectUri).accept(MediaType.APPLICATION_JSON)
.header("authorization", authorization)
.param("openId", "none"))
.andExpect(status().isOk())
.andReturn();
content = mvcResult.getResponse().getContentAsString();
jsonResult = JSON.parseObject(new String(content));
code = jsonResult.get("code");
Assert.assertEquals("0000", code);
businessCode = jsonResult.get("businessCode");
Assert.assertEquals("0002", businessCode);
} }
/** /**
...@@ -128,8 +80,8 @@ public class LoginWithTokenOnceTests { ...@@ -128,8 +80,8 @@ public class LoginWithTokenOnceTests {
@Test @Test
public void testLoginFastFoH5() throws Exception{ public void testLoginFastFoH5() throws Exception{
// 获取TokenOnce // 获取TokenOnce
String tokenOnceUri = "/token/once"; String oneTimeTokenUri = "/token/oneTime";
MvcResult mvcResult = mvc.perform(MockMvcRequestBuilders.get(tokenOnceUri).accept(MediaType.APPLICATION_JSON) MvcResult mvcResult = mvc.perform(MockMvcRequestBuilders.get(oneTimeTokenUri).accept(MediaType.APPLICATION_JSON)
.param("phoneNo", phoneNo)) .param("phoneNo", phoneNo))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andReturn(); .andReturn();
...@@ -139,11 +91,11 @@ public class LoginWithTokenOnceTests { ...@@ -139,11 +91,11 @@ public class LoginWithTokenOnceTests {
Assert.assertEquals("0000", code); Assert.assertEquals("0000", code);
Object data = jsonResult.get("data"); Object data = jsonResult.get("data");
Assert.assertNotNull(data); Assert.assertNotNull(data);
StringBuilder tokenBuilder = new StringBuilder(phoneNo); String oneTimeToken = String.valueOf(data);
String tokenOnce = new String(Base64.getEncoder().encodeToString(tokenBuilder.append(":").append(data).toString().getBytes(Charset.forName("UTF-8")))); Assert.assertNotEquals("", oneTimeToken);
String smsUri = "/api/sms/send_login_code"; String smsUri = "/api/sms/send_login_code";
mvcResult = mvc.perform(MockMvcRequestBuilders.get(tokenOnceUri).accept(MediaType.APPLICATION_JSON) mvcResult = mvc.perform(MockMvcRequestBuilders.get(oneTimeTokenUri).accept(MediaType.APPLICATION_JSON)
.param("phoneNo", phoneNo)) .param("phoneNo", phoneNo))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andReturn(); .andReturn();
...@@ -159,7 +111,7 @@ public class LoginWithTokenOnceTests { ...@@ -159,7 +111,7 @@ public class LoginWithTokenOnceTests {
// 第一次使用TokenOnce // 第一次使用TokenOnce
String aspectUri = "/user/login/fastForH5"; String aspectUri = "/user/login/fastForH5";
mvcResult = mvc.perform(MockMvcRequestBuilders.get(aspectUri).accept(MediaType.APPLICATION_JSON) mvcResult = mvc.perform(MockMvcRequestBuilders.get(aspectUri).accept(MediaType.APPLICATION_JSON)
.header("TokenOnce", tokenOnce) .header(Constants.ONE_TIME_TOKEN, oneTimeToken)
.header("authorization", authorization)) .header("authorization", authorization))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andReturn(); .andReturn();
...@@ -168,10 +120,10 @@ public class LoginWithTokenOnceTests { ...@@ -168,10 +120,10 @@ public class LoginWithTokenOnceTests {
code = jsonResult.get("code"); code = jsonResult.get("code");
Object businessCode = jsonResult.get("businessCode"); Object businessCode = jsonResult.get("businessCode");
Assert.assertEquals("0000", code); Assert.assertEquals("0000", code);
Assert.assertNotEquals("0001", businessCode); Assert.assertNotEquals("0002", businessCode);
// 使用过期的TokenOnce与verificationCode // 使用过期的TokenOnce与verificationCode
mvcResult = mvc.perform(MockMvcRequestBuilders.get(aspectUri).accept(MediaType.APPLICATION_JSON) mvcResult = mvc.perform(MockMvcRequestBuilders.get(aspectUri).accept(MediaType.APPLICATION_JSON)
.header("TokenOnce", tokenOnce) .header(Constants.ONE_TIME_TOKEN, oneTimeToken)
.header("authorization", authorization)) .header("authorization", authorization))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andReturn(); .andReturn();
......
package token; package token;
import cn.quantgroup.xyqb.Bootstrap; import cn.quantgroup.xyqb.Bootstrap;
import cn.quantgroup.xyqb.Constants;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
...@@ -27,7 +28,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. ...@@ -27,7 +28,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
@RunWith(SpringJUnit4ClassRunner.class) @RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Bootstrap.class) @SpringApplicationConfiguration(classes = Bootstrap.class)
@WebAppConfiguration @WebAppConfiguration
public class TokenOnceTests { public class OneTimeTokenTests {
final String phoneNo = "13461067662"; final String phoneNo = "13461067662";
private MockMvc mvc; private MockMvc mvc;
...@@ -48,70 +49,54 @@ public class TokenOnceTests { ...@@ -48,70 +49,54 @@ public class TokenOnceTests {
public void testServer() throws Exception { public void testServer() throws Exception {
mvc.perform(MockMvcRequestBuilders.get("/").accept(MediaType.APPLICATION_JSON)) mvc.perform(MockMvcRequestBuilders.get("/").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk()); .andExpect(status().isOk());
mvc.perform(MockMvcRequestBuilders.get("/token/once").accept(MediaType.APPLICATION_JSON)) mvc.perform(MockMvcRequestBuilders.get("/token/oneTime").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk()); .andExpect(status().isOk());
} }
/** /**
* 测试TokenOnce发放服务 * 测试OneTime-Token发放服务
* @throws Exception * @throws Exception
*/ */
@Test @Test
public void testTokenOnce() throws Exception{ public void testTokenOnce() throws Exception{
String tokenOnceUri = "/token/once"; String tokenOnceUri = "/token/oneTime";
MvcResult mvcResult = mvc.perform(MockMvcRequestBuilders.get(tokenOnceUri).accept(MediaType.APPLICATION_JSON) MvcResult mvcResult = mvc.perform(MockMvcRequestBuilders.get(tokenOnceUri).accept(MediaType.APPLICATION_JSON))
.header("Session-Id", "82107d0326772b8b5c72ec11801b8ab3"))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andReturn(); .andReturn();
String content = mvcResult.getResponse().getContentAsString(); String content = mvcResult.getResponse().getContentAsString();
JSONObject jsonResult = JSON.parseObject(new String(content)); JSONObject jsonResult = JSON.parseObject(new String(content));
Object code = jsonResult.get("code"); Object code = jsonResult.get("code");
Assert.assertEquals("0000", code); Assert.assertEquals("0000", code);
Object data = jsonResult.get("data"); Object data = jsonResult.getString("data");
Assert.assertEquals(data, "");
Object msg = jsonResult.get("msg");
Assert.assertEquals("获取TokenOnce失败", jsonResult.get("msg"));
jsonResult = JSON.parseObject(new String(content));
mvcResult = mvc.perform(MockMvcRequestBuilders.get(tokenOnceUri).accept(MediaType.APPLICATION_JSON)
.param("phoneNo", phoneNo))
.andExpect(status().isOk())
.andReturn();
content = mvcResult.getResponse().getContentAsString();
jsonResult = JSON.parseObject(new String(content));
code = jsonResult.get("code");
Assert.assertEquals("0000", code);
data = jsonResult.get("data");
Assert.assertNotNull(data); Assert.assertNotNull(data);
msg = jsonResult.get("msg"); String oneTimeToken = String.valueOf(data);
Assert.assertEquals(msg, ""); Assert.assertNotEquals("", oneTimeToken);
} }
/** /**
* 测试TokenOnce切面 * 测试OneTime-Token切面
* @throws Exception * @throws Exception
*/ */
// TODO 用户注册先不加TokenOnce校验,进一步确认后再添加或删除本用例 // TODO 用户注册先不加OneTime-Token校验,进一步确认后再添加或删除本用例
//@Test //@Test
public void testAspect() throws Exception{ public void testAspect() throws Exception{
// 获取TokenOnce // 获取OneTime-Token
String tokenOnceUri = "/token/once"; String oneTimeTokenUri = "/token/oneTime";
MvcResult mvcResult = mvc.perform(MockMvcRequestBuilders.get(tokenOnceUri).accept(MediaType.APPLICATION_JSON) MvcResult mvcResult = mvc.perform(MockMvcRequestBuilders.get(oneTimeTokenUri).accept(MediaType.APPLICATION_JSON))
.param("phoneNo", phoneNo))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andReturn(); .andReturn();
String content = mvcResult.getResponse().getContentAsString(); String content = mvcResult.getResponse().getContentAsString();
JSONObject jsonResult = JSON.parseObject(new String(content)); JSONObject jsonResult = JSON.parseObject(new String(content));
Object code = jsonResult.get("code"); Object code = jsonResult.get("code");
Assert.assertEquals("0000", code); Assert.assertEquals("0000", code);
Object data = jsonResult.get("data"); Object data = jsonResult.getString("data");
Assert.assertNotNull(data); Assert.assertNotNull(data);
StringBuilder tokenBuilder = new StringBuilder(phoneNo); String oneTimeToken = String.valueOf(data);
String tokenOnce = new String(Base64.getEncoder().encodeToString(tokenBuilder.append(":").append(data).toString().getBytes(Charset.forName("UTF-8")))); Assert.assertNotEquals("", oneTimeToken);
// 第一次使用TokenOnce // 第一次使用OneTime-Token
String aspectUri = "/user/register"; String aspectUri = "/user/loginForH5";
mvcResult = mvc.perform(MockMvcRequestBuilders.get(aspectUri).accept(MediaType.APPLICATION_JSON) mvcResult = mvc.perform(MockMvcRequestBuilders.get(aspectUri).accept(MediaType.APPLICATION_JSON)
.header("TokenOnce", tokenOnce) .header(Constants.ONE_TIME_TOKEN, oneTimeToken)
.param("phoneNo", phoneNo) .param("phoneNo", phoneNo)
.param("password", "Qg123456") .param("password", "Qg123456")
.param("verificationCode", "1234")) .param("verificationCode", "1234"))
...@@ -125,7 +110,7 @@ public class TokenOnceTests { ...@@ -125,7 +110,7 @@ public class TokenOnceTests {
Assert.assertNotEquals("0002", businessCode); Assert.assertNotEquals("0002", businessCode);
// 使用过期的TokenOnce // 使用过期的TokenOnce
mvcResult = mvc.perform(MockMvcRequestBuilders.get(aspectUri).accept(MediaType.APPLICATION_JSON) mvcResult = mvc.perform(MockMvcRequestBuilders.get(aspectUri).accept(MediaType.APPLICATION_JSON)
.header("TokenOnce", tokenOnce) .header("TokenOnce", oneTimeToken)
.param("phoneNo", phoneNo) .param("phoneNo", phoneNo)
.param("password", "Qg123456") .param("password", "Qg123456")
.param("verificationCode", "1234")) .param("verificationCode", "1234"))
......
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