REM:
1. 更新协议定义
2. 更新协议参数校验方法
This commit is contained in:
HuangXin 2020-07-24 10:23:49 +08:00
parent a41e540401
commit 60ccb0e654
15 changed files with 468 additions and 20 deletions

View File

@ -40,7 +40,7 @@ public class ConstValue {
/** /**
* The constant VERSION. * The constant VERSION.
*/ */
public static final int VERSION = 2; public static final int VERSION = 3;
/** /**
* The constant CRYPTO_NONE. * The constant CRYPTO_NONE.
*/ */

View File

@ -0,0 +1,48 @@
package com.dispose.controller;
import com.dispose.common.ErrorCode;
import com.dispose.pojo.dto.protocol.auth.LoginReq;
import com.dispose.pojo.dto.protocol.auth.LoginRsp;
import com.dispose.pojo.dto.protocol.base.ProtocolReqDTO;
import com.dispose.pojo.dto.protocol.base.ProtocolRespDTO;
import com.dispose.service.UserAccountService;
import com.fasterxml.jackson.core.JsonProcessingException;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.annotation.Resource;
import javax.validation.Valid;
/**
* The type Auth controller.
*
* @author <huangxin@cmhi.chinamoblie.com>
*/
@Controller
@RequestMapping(value = "/auth")
@Slf4j
@Api(value = "处置平台认证接口", tags = "处置平台认证接口")
@Component
@Validated
public class AuthController {
@Resource
private UserAccountService userAccountService;
@PostMapping("/login")
@ResponseBody
@ApiOperation("登录")
public ProtocolRespDTO<LoginRsp> userLogin(@RequestBody @Valid ProtocolReqDTO<LoginReq> mr) throws JsonProcessingException {
return ProtocolRespDTO.result(ErrorCode.ERR_OK,
LoginRsp.builder()
.token("1234576")
.build());
}
}

View File

@ -0,0 +1,48 @@
package com.dispose.exception;
import com.dispose.common.ErrorCode;
import com.dispose.pojo.dto.protocol.base.BaseRespStatus;
import com.dispose.pojo.dto.protocol.base.ProtocolRespDTO;
import jodd.net.HttpStatus;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
/**
* The type Global exception handler.
*
* @author <huangxin@cmhi.chinamoblie.com>
*/
@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
/**
* Handle exception protocol resp dto.
*
* @param ex the ex
* @return the protocol resp dto
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseBody
public ProtocolRespDTO<BaseRespStatus> handleException(MethodArgumentNotValidException ex) {
log.error("Exception: {}", ex.getMessage());
List<String> exMsg = new ArrayList<>();
AtomicInteger idx = new AtomicInteger();
ex.getBindingResult()
.getAllErrors()
.forEach(v -> exMsg.add(idx.getAndIncrement() + ": " + v.getDefaultMessage()));
return ProtocolRespDTO.result(ErrorCode.ERR_PARAMEXCEPTION,
HttpStatus.error400().status(),
exMsg.toArray(new String[0]));
}
}

View File

@ -122,4 +122,13 @@ public interface UserAccountManager {
* @return the error code * @return the error code
*/ */
ErrorCode verifyTokenPermission(String token); ErrorCode verifyTokenPermission(String token);
/**
* Verify user login error code.
*
* @param username the username
* @param token the token
* @return the error code
*/
ErrorCode verifyUserLogin(String username, String token);
} }

View File

@ -68,7 +68,7 @@ public class UserAccountManagerImpl implements UserAccountManager {
public UserAccount getUserByName(String username) { public UserAccount getUserByName(String username) {
// 根据用户名在缓存中查找用户 // 根据用户名在缓存中查找用户
Optional<UserAccount> findRet = userAccountCache.values().stream() Optional<UserAccount> findRet = userAccountCache.values().stream()
.filter(userAccountCache -> username.equals(userAccountCache.getUsername())) .filter(userAccountCache -> userAccountCache.getUsername().equals(username))
.findFirst(); .findFirst();
return findRet.orElseGet(() -> userAccountMapper.getUserByName(username)); return findRet.orElseGet(() -> userAccountMapper.getUserByName(username));
@ -401,6 +401,49 @@ public class UserAccountManagerImpl implements UserAccountManager {
return ErrorCode.ERR_PERMISSION; return ErrorCode.ERR_PERMISSION;
} }
/**
* Verify user login error code.
*
* @param username the username
* @param token the token
* @return the error code
*/
@Override
public ErrorCode verifyUserLogin(String username, String token) {
UserAccount user;
// 本机曾经登录过
if (userAccountCache.containsKey(token)) {
user = userAccountCache.get(token);
// 超时
if(tokenTimeout(user.getLastAccess())) {
return ErrorCode.ERR_TOKENTIMEOUT;
}
return ErrorCode.ERR_OK;
} else {
user = userAccountMapper.getUserByToken(token);
// 用户未登录或者已经注销
if(user == null) {
return ErrorCode.ERR_LOGOUT;
}
// 用户未登录或者已经注销
if(user.getToken().length() == 0) {
return ErrorCode.ERR_LOGOUT;
}
if(tokenTimeout(user.getLastAccess())) {
return ErrorCode.ERR_TOKENTIMEOUT;
}
return ErrorCode.ERR_OK;
}
}
/** /**
* Token timeout boolean. * Token timeout boolean.
* *

View File

@ -78,9 +78,8 @@ public interface UserAccountMapper extends Mapper<UserAccount>,
* Upgrade login time string. * Upgrade login time string.
* *
* @param username the username * @param username the username
* @return the string
*/ */
String upgradeLoginTime(@Param("username") String username); void upgradeLoginTime(@Param("username") String username);
/** /**
* Upgrade last access time string. * Upgrade last access time string.

View File

@ -0,0 +1,34 @@
package com.dispose.pojo.dto.protocol.auth;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotBlank;
/**
* The type Login info.
*
* @author <huangxin@cmhi.chinamoblie.com>
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@JsonInclude(JsonInclude.Include.NON_NULL)
public class LoginReq {
/**
* The User name.
*/
@NotBlank(message = "userName 用户名不能为空")
private String userName;
/**
* The Password.
*/
@NotBlank(message = "password 密码不能为空")
@Length(min = 64, max = 64, message = "password 密码长度必须为SHA256编码后的长度")
private String password;
}

View File

@ -0,0 +1,44 @@
package com.dispose.pojo.dto.protocol.auth;
import com.dispose.pojo.dto.protocol.base.BaseRespStatus;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
/**
* The type Login rsp.
*
* @author <huangxin@cmhi.chinamoblie.com>
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonPropertyOrder({"userName", "token", "logTime", "expireTime", "status", "message"})
public class LoginRsp extends BaseRespStatus {
/**
* The User name.
*/
private String userName;
/**
* The Token.
*/
private String token;
/**
* The Log time.
*/
private Long logTime;
/**
* The Expire time.
*/
private Long expireTime;
}

View File

@ -0,0 +1,47 @@
package com.dispose.pojo.dto.protocol.base;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.validator.constraints.Range;
import javax.validation.Valid;
import javax.validation.constraints.DecimalMin;
import javax.validation.constraints.NotNull;
/**
* The type Base protocol dto.
*
* @author <huangxin@cmhi.chinamoblie.com>
*/
@Data
@NoArgsConstructor
@ApiModel("通信协议实体")
@JsonPropertyOrder({"ver", "cryptoType", "timeStamp", "msgContent"})
public class BaseProtocolDTO<T> {
@ApiModelProperty(value = "协议版本号", required = true, example = "1")
@NotNull(message = "ver 字段不能为空")
@Range(min = 3, max = 9999, message = "ver 字段最小值为 3")
private Integer ver;
@ApiModelProperty(value = "msgContent字段内容编码格式\n" +
"0无编码格式普通字符串\n" +
"1base64编码格式\n" +
"2采用AES加密后的base64编码格式\n", required = true,
allowableValues = "0, 1, 2",
example = "0")
@NotNull(message = "cryptoType 字段不能为空")
@Range(min = 0, max = 2, message = "cryptoType 字段取值为 [0, 2]")
private Integer cryptoType;
@ApiModelProperty(value = "当前UTC时间戳", required = true, example = "1526625689000")
@NotNull(message = "timeStamp 字段不能为空")
@DecimalMin(value = "1595494343000", message = "timeStamp 字段值不能为过去时间")
private Long timeStamp;
@ApiModelProperty(value = "协议详细内容\n", example = "{}")
@Valid
private T msgContent;
}

View File

@ -0,0 +1,37 @@
package com.dispose.pojo.dto.protocol.base;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Arrays;
/**
* The type Response status.
*
* @author <huangxin@cmhi.chinamoblie.com>
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class BaseRespStatus {
/**
* The Status.
*/
private Integer status;
/**
* The Message.
*/
private String[] message;
/**
* To string string.
*
* @return the string
*/
@Override
public String toString() {
return "{\"status\":" + status + ", \"message\":\"" + Arrays.toString(message) + "\"}";
}
}

View File

@ -0,0 +1,16 @@
package com.dispose.pojo.dto.protocol.base;
import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
/**
* The type Protocol req dto.
*
* @author <huangxin@cmhi.chinamoblie.com>
*/
@NoArgsConstructor
@ToString
@Slf4j
public class ProtocolReqDTO<T> extends BaseProtocolDTO<T> {
}

View File

@ -0,0 +1,113 @@
package com.dispose.pojo.dto.protocol.base;
import com.dispose.common.ConstValue;
import com.dispose.common.ErrorCode;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
/**
* The type Protocol resp dto.
*
* @param <T> the type parameter
* @author <huangxin@cmhi.chinamoblie.com>
*/
@EqualsAndHashCode(callSuper = true)
@Data
@NoArgsConstructor
@AllArgsConstructor
@JsonPropertyOrder({"ver", "cryptoType", "timeStamp", "code", "msgContent"})
public class ProtocolRespDTO<T> extends BaseProtocolDTO<T> {
/**
* The constant OBJECT_MAPPER.
*/
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
/**
* The Code.
*/
@ApiModelProperty(value = "服务器返回状态码", example = "200")
private Integer code;
/**
* Result protocol resp dto.
*
* @param <T> the type parameter
* @param err the err
* @param obj the obj
* @return the protocol resp dto
*/
public static <T> ProtocolRespDTO<T> result(ErrorCode err, T obj) {
return result(err, obj, ConstValue.Protocol.CRYPTO_NONE);
}
/**
* Result protocol resp dto.
*
* @param <T> the type parameter
* @param err the err
* @param respMsg the resp msg
* @param crypto the crypto
* @return the protocol resp dto
*/
public static <T> ProtocolRespDTO<T> result(ErrorCode err, T respMsg, Integer crypto) {
ProtocolRespDTO<T> resp = new ProtocolRespDTO<>();
resp.setVer(ConstValue.Protocol.VERSION);
resp.setCode(err.getHttpCode());
resp.setCryptoType(ConstValue.Protocol.CRYPTO_NONE);
resp.setTimeStamp(System.currentTimeMillis());
resp.setMsgContent(respMsg);
return resp;
}
/**
* Result protocol resp dto.
*
* @param err the err
* @return the protocol resp dto
*/
public static ProtocolRespDTO<BaseRespStatus> result(ErrorCode err) {
ProtocolRespDTO<BaseRespStatus> resp = new ProtocolRespDTO<>();
BaseRespStatus rspMsg = new BaseRespStatus();
rspMsg.setStatus(err.getCode());
rspMsg.setMessage(new String[] {err.getMsg()});
resp.setVer(ConstValue.Protocol.VERSION);
resp.setCode(err.getHttpCode());
resp.setCryptoType(ConstValue.Protocol.CRYPTO_NONE);
resp.setTimeStamp(System.currentTimeMillis());
resp.setMsgContent(rspMsg);
return resp;
}
/**
* Result protocol resp dto.
*
* @param errCode the err code
* @param httpCode the http code
* @param message the message
* @return the protocol resp dto
*/
public static ProtocolRespDTO<BaseRespStatus> result(ErrorCode errCode, Integer httpCode, String[] message) {
ProtocolRespDTO<BaseRespStatus> resp = new ProtocolRespDTO<>();
BaseRespStatus rspMsg = new BaseRespStatus();
rspMsg.setStatus(errCode.getCode());
rspMsg.setMessage(message);
resp.setVer(ConstValue.Protocol.VERSION);
resp.setCode(httpCode);
resp.setCryptoType(ConstValue.Protocol.CRYPTO_NONE);
resp.setTimeStamp(System.currentTimeMillis());
resp.setMsgContent(rspMsg);
return resp;
}
}

View File

@ -121,10 +121,12 @@ public class UserAccountServiceImpl implements UserAccountService {
return ErrorCode.ERR_USERNOTFOUND; return ErrorCode.ERR_USERNOTFOUND;
} }
ErrorCode err = userAccountManager.verifyUserLogin(username, token);
// 注销 // 注销
userAccountManager.cleanUserToken(username); userAccountManager.cleanUserToken(username);
return ErrorCode.ERR_OK; return err;
} }
/** /**

View File

@ -41,16 +41,12 @@
WHERE username = #{username, jdbcType=VARCHAR} WHERE username = #{username, jdbcType=VARCHAR}
</select> </select>
<select id="upgradeLoginTime" resultType="java.lang.String"> <update id="upgradeLoginTime">
UPDATE UPDATE
user_account user_account
SET lastLoginTime = CURRENT_TIMESTAMP SET lastLoginTime = CURRENT_TIMESTAMP
WHERE username = #{username, jdbcType=VARCHAR};
SELECT lastLoginTime
FROM user_account
WHERE username = #{username, jdbcType=VARCHAR} WHERE username = #{username, jdbcType=VARCHAR}
</select> </update>
<select id="upgradeToken" resultType="java.lang.String"> <select id="upgradeToken" resultType="java.lang.String">
UPDATE UPDATE

View File

@ -1,6 +1,7 @@
package com.dispose.test.service; package com.dispose.test.service;
import com.dispose.common.ErrorCode; import com.dispose.common.ErrorCode;
import com.dispose.config.DisposeConfigure;
import com.dispose.pojo.entity.UserAccount; import com.dispose.pojo.entity.UserAccount;
import com.dispose.pojo.po.MulReturnType; import com.dispose.pojo.po.MulReturnType;
import com.dispose.service.UserAccountService; import com.dispose.service.UserAccountService;
@ -22,6 +23,8 @@ import java.security.NoSuchAlgorithmException;
/** /**
* The type User account service test. * The type User account service test.
*
* @author <huangxin@cmhi.chinamoblie.com>
*/ */
@RunWith(SpringRunner.class) @RunWith(SpringRunner.class)
@SpringBootTest @SpringBootTest
@ -40,6 +43,12 @@ public class UserAccountServiceTest extends InitTestEnvironment {
@Resource @Resource
private UserAccountService userAccountService; private UserAccountService userAccountService;
/**
* The Dispose configure.
*/
@Resource
private DisposeConfigure disposeConfigure;
/** /**
* User login test. * User login test.
* *
@ -56,12 +65,12 @@ public class UserAccountServiceTest extends InitTestEnvironment {
} }
/** /**
* T 1 log service test. * A 1 log service test.
* *
* @throws NoSuchAlgorithmException the no such algorithm exception * @throws NoSuchAlgorithmException the no such algorithm exception
*/ */
@Test @Test
public void t1_logServiceTest() throws NoSuchAlgorithmException { public void a1_logServiceTest() throws NoSuchAlgorithmException {
MulReturnType<ErrorCode, String> ret = userAccountService.loginService(getUSER_NAME(), MulReturnType<ErrorCode, String> ret = userAccountService.loginService(getUSER_NAME(),
getPASSWORD()); getPASSWORD());
@ -78,10 +87,10 @@ public class UserAccountServiceTest extends InitTestEnvironment {
} }
/** /**
* T 2 logout service test. * A 2 logout service test.
*/ */
@Test @Test
public void t2_logoutServiceTest() { public void a2_logoutServiceTest() {
ErrorCode err = userAccountService.logoutService(getUSER_NAME(), token); ErrorCode err = userAccountService.logoutService(getUSER_NAME(), token);
Assert.assertEquals(err, ErrorCode.ERR_OK); Assert.assertEquals(err, ErrorCode.ERR_OK);
@ -90,21 +99,24 @@ public class UserAccountServiceTest extends InitTestEnvironment {
} }
/** /**
* T 3 get user by token test. * A 3 get user by token test.
*/ */
@Test @Test
public void t3_getUserByTokenTest() { public void a3_getUserByTokenTest() {
UserAccount username = userAccountService.getUserByToken(UserAccountServiceTest.token); UserAccount username = userAccountService.getUserByToken(UserAccountServiceTest.token);
Assert.assertEquals(username.getUsername(), getUSER_NAME()); Assert.assertEquals(username.getUsername(), getUSER_NAME());
Assert.assertNull(userAccountService.getUserByToken(UserAccountServiceTest.token + "1235")); Assert.assertNull(userAccountService.getUserByToken(UserAccountServiceTest.token + "1235"));
} }
/** /**
* T 4 auth token check test. * A 4 auth token check test.
*/ */
@Test @Test
public void t4_authTokenCheckTest() { public void a4_authTokenCheckTest() {
Assert.assertEquals(userAccountService.authTokenCheck(token), ErrorCode.ERR_OK); Assert.assertEquals(userAccountService.authTokenCheck(token), ErrorCode.ERR_OK);
Assert.assertEquals(userAccountService.authTokenCheck(token + "1235"), ErrorCode.ERR_LOGOUT);
if (String.valueOf(true).equals(disposeConfigure.getCheckRequestToken())) {
Assert.assertEquals(userAccountService.authTokenCheck(token + "1235"), ErrorCode.ERR_LOGOUT);
}
} }
} }