登录接口
@AuditLogEnregistor(module=AuditLogEvent.MODULE_LOGON,action="登录")
@ApiOperation(value="用户登录", httpMethod="POST")
@RequestMapping(value="/login", method=RequestMethod.POST)
public ApiResultDTO<AccessTokenUser> login(@RequestBody LoginVO vo, HttpServletRequest hreq,
HttpServletResponse hrep){
return RestAPITemplate.restapi(() -> {
String userIP = hreq.getRemoteAddr();
LoginVO newvo;
if(StringUtils.isNotBlank(vo.getSm4Json())) {
newvo = JsonConverter.jsonStrToObject(SM4Utils.decryptBase64ToStr(vo.getSm4Json(), null), LoginVO.class);
}else {
newvo = vo;
}
PrivilegesUser loginUser = null;
try {
loginUser = ThreadLocalCache.fetchAPIData(null,
() -> {
return sysGatewayService.getPrivilegesUserByAccount_dontCache(newvo.getAccount(), true);
}
);
} catch (Exception e) {
logger.error("获取用户服务出错", e);
throw new RuntimeException("获取用户服务出错");
}
if(loginUser==null){
throw new RuntimeException("无效的用户名");
}
validUserPassword(loginUser, newvo.getPassword(), userIP);
AccessTokenUser tokenUser=loginUser.buildAccessTokenUser(null);
int maxInactiveInterval;
boolean forced = false;
if (XysdConstants.TOKEN_FORMTYPE_APP.equals(vo.getFromType())) {
forced = true;
maxInactiveInterval = Integer.parseInt(BaseConstants.getProperty("maxInactiveIntervalWithApp",(60*24)+""));
} else {
maxInactiveInterval = Integer.parseInt(BaseConstants.getProperty("maxInactiveInterval",(60*24)+""));
}
AccessTokenGenerateVO tokenGenerateVO = new AccessTokenGenerateVO(tokenUser, XysdConstants.TOKEN_FORMTYPE_PC,
null, forced, maxInactiveInterval);
String token=ThreadLocalCache.fetchAPIData(null, ()->{
return tokenService.generateAccessToken(tokenGenerateVO);
}, "生成访问令牌出错,");
tokenUser.setAccessToken(token);
tokenUser.addAttr(AccessTokenUser.ATTR_FROM_TYPE,(forced?XysdConstants.TOKEN_FORMTYPE_APP:XysdConstants.TOKEN_FORMTYPE_PC));
if (ValueObjectEntity.SYSCODE_HDXF.equals(BaseConstants.getProperty("syscode",null))){
Long begin = loginUser.getPwdLastUpdateTime();
long end = new Date().getTime();
if(begin!=null&&end>=begin) {
long days = (end - begin )/1000/60/60/24;
if(days>90) {
OtherData userInfo = tokenUser.getUserInfo();
if(userInfo==null) tokenUser.setUserInfo(new OtherData());
userInfo.put("pwdRemind", "您已连续使用该口令"+days+"天,请尽快更换口令。");
}
}
}
this.addOtherOrgUsers(tokenUser,token);
LoginInitUtils.afterLogin(true, hreq, hrep, tokenUser);
return tokenUser;
});
}
private void validUserPassword(PrivilegesUser privilegesUser, String password, String userIP) {
String errorMsg=null;
AuditLogEvent auditLogEvent = null;
if (privilegesUser.isLocked()){
if (privilegesUser.hasResource(AuditLogEvent.CATEGORY_SAFETYMANAGER)){
long begin = privilegesUser.getLockedTime();
long end = new Date().getTime();
long minute = (end - begin )/1000/60;
if (minute<30){
long timeMin=30-minute;
auditLogEvent=new AuditLogEvent(Utils.getUUID(""),new Date(),AuditLogEvent.MODULE_LOGON
,"登录失败:用户:"+privilegesUser.getUser().getName()+"已被锁定,"+timeMin+"分后解锁。"
,"/login",userIP,privilegesUser.buildAccessTokenUser(null),null,"失败",null,null);
errorMsg="登录失败:该用户已被锁定,"+timeMin+"分后解锁。";
}else {
sysGatewayService.unLockedUser(privilegesUser.getUser().getId());
}
}else{
auditLogEvent=new AuditLogEvent(Utils.getUUID(""),new Date(),AuditLogEvent.MODULE_LOGON
,"登录失败:用户:"+privilegesUser.getUser().getName()+"已被锁定","/login"
,userIP,privilegesUser.buildAccessTokenUser(null),null,"失败",null,null);
errorMsg="登录失败次数过多,已被锁定,请联系管理员解锁";
}
}else if(privilegesUser.getPassword()!=null){
logger.info("=====开始加密======:["+password+"]");
if(!privilegesUser.getPassword().equals(SM3Utils.sm3(password))) {
sysGatewayService.addUserPwdWrongCountById_dontCache(privilegesUser.getUser().getId());
int nowWrongPwdCount=privilegesUser.getWrongPwdCount()+1;
if (nowWrongPwdCount>=5){
auditLogEvent=new AuditLogEvent(Utils.getUUID(""),new Date(),AuditLogEvent.MODULE_LOGON
,"登录失败:用户:"+privilegesUser.getUser().getName()+"密码连续输入错误次数超过"
+5+"次,该用户已被锁定。"
,"/login",userIP,privilegesUser.buildAccessTokenUser(null),null,"失败",null,null);
sysGatewayService.lockedUser(privilegesUser.getUser().getId());
errorMsg="登录失败次数过多,已被锁定,请联系管理员解锁";
}else {
auditLogEvent=new AuditLogEvent(Utils.getUUID(""),new Date(),AuditLogEvent.MODULE_LOGON
,"登录失败用户:"+privilegesUser.getUser().getName()+"密码错误 。","/login"
,userIP,privilegesUser.buildAccessTokenUser(null),null,"失败",null,null);
errorMsg="密码连续输入错误"+nowWrongPwdCount+"次,超过"+5+"次后用户锁定。";
}
}else {
sysGatewayService.initUserPwdWrongCount(privilegesUser.getUser().getId());
}
}else if(privilegesUser.getPassword()==null && null==password){
sysGatewayService.initUserPwdWrongCount(privilegesUser.getUser().getId());
}else {
errorMsg = "无效的用户";
}
if (auditLogEvent!=null){
DomainEventPublisherFactory.getRegisteredPublisher().publishEvent(auditLogEvent);
}
if(StringUtils.isNotBlank(errorMsg)){
throw new RuntimeException(errorMsg);
}
}
private void addOtherOrgUsers(AccessTokenUser tokenUser,String token){
if(!ValueObjectEntity.SYSCODE_GJJTXF.equals(BaseConstants.getProperty("syscode",null))){
return;
}
if(StringUtils.isBlank(tokenUser.obtainSyncUserRealId())){
return;
}
List<PrivilegesUser> loginUsers;
try {
loginUsers = ThreadLocalCache.fetchAPIData(null, new IDataFetch<ApiResultDTO<List<PrivilegesUser>>>() {
@Override
public ApiResultDTO<List<PrivilegesUser>> fetchData() {
return sysGatewayService.getAllPrivilegesUserBySyncPartyIdAndDataFrom(tokenUser.obtainSyncUserRealId(), null);
}
});
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("获取用户服务出错");
}
if (CollectionUtils.isEmpty(loginUsers)) {
return;
}
List<PrivilegesUser> otherLoginUsers = loginUsers.stream()
.filter(l->!l.getUser().getId().equals(tokenUser.getUserId()))
.collect(Collectors.toList());
if(CollectionUtils.isNotEmpty(otherLoginUsers)){
if(tokenUser.getUserInfo()==null){
tokenUser.setUserInfo(new OtherData());
}
List<SysDataSimpleValObj> otherOrgUsers = otherLoginUsers.stream()
.map(o->{
o.getUser().putOtherData("ticket",token);
o.getUser().putOtherData("syncPartyId", tokenUser.obtainSyncUserRealId());
o.getUser().putOtherData("org",o.getOrg());
o.getUser().putOtherData("dept",o.getDept());
return o.getUser();
})
.collect(Collectors.toList());
tokenUser.getUserInfo().put(AccessTokenUser.ATTR_OTHER_ORG_USER,otherOrgUsers);
}
}
根据用户信息生成token
@ApiOperation(value="根据信息生成访问令牌", httpMethod="POST")
@RequestMapping(value="/generate", method=RequestMethod.POST)
public ApiResultDTO<String> generateAccessToken(@RequestBody AccessTokenGenerateVO vo){
return RestAPITemplate.restapi(()->{
return tokenService.createAccessToken(vo.getUser(), vo.getUser().getAccount(),
vo.getFromType(), vo.getDeviceId(), vo.isForced(), vo.getExpireMinutes());
});
}
public String createAccessToken(AccessTokenUser user, String account, String fromType,
String deviceId, boolean forced, int expireMinutes
) {
if("before".equals(BaseConstants.getProperty("kickOutUserType",""))){
tokenRepository.deleteTokenBy(user.getUser().getId(),fromType);
}else if("after".equals(BaseConstants.getProperty("kickOutUserType",""))){
List<Token> exists=tokenRepository.findValidTokensByUserId(user.getUser().getId(), fromType);
if(exists!=null&&exists.size()>0){
throw new RuntimeException("您已在其他地址登录!");
}
}
if(!XysdConstants.TOKEN_FORMTYPE_PC.equals(fromType)&&forced){
List<String> exists=tokenRepository.findDeviceIdsBy(user.getUser().getId(), fromType);
if(exists!=null&&exists.size()>0){
StringBuffer deviceIds=new StringBuffer();
for (String t : exists) {
if(StringUtils.isNotBlank(t)
&&!t.equals(deviceId)){
if(deviceIds.length()>0){
deviceIds.append(",");
}
deviceIds.append(t);
}
}
tokenRepository.deleteTokenBy(user.getUser().getId(), fromType);
if(XysdConstants.TOKEN_FORMTYPE_APP.equals(fromType)&&deviceIds.length()>0){
}
}
}
try {
if(!XysdConstants.TOKEN_FORMTYPE_PC.equals(fromType)&&!forced){
tokenRepository.deleteTokenBy(user.getUser().getId(),fromType);
}
Token token=Token.buildToken(fromType, expireMinutes, user, account, deviceId);
tokenRepository.create(token);
AccessTokenUserCache ac=new AccessTokenUserCache(token);
getCache().put(ac.getAccess_token(), ac);
return token.getId();
} catch (Exception e) {
if(e.getCause() instanceof ConstraintViolationException){
throw new RuntimeException("令牌已存在");
}
e.printStackTrace();
throw e;
}
}
构建token
public static Token buildToken(String fromType, int expireMinutes, AccessTokenUser user,
String account, String deviceId) {
String tokenId = Utils.getUUID("");
if(StringUtils.isNotBlank(user.getAccessToken())) {
tokenId=user.getAccessToken();
}
String tokenSgin = user.getUser().getId()+"_"+fromType;
if(!XysdConstants.TOKEN_FORMTYPE_APP.equals(fromType)
||!XysdConstants.TOKEN_FORMTYPE_WECHATAPPLET.equals(fromType)
){
tokenSgin+="_"+Utils.getUUID("");
}
Token token = new Token(tokenId, expireMinutes, user.getUser(), account,
fromType, deviceId, tokenSgin, JsonConverter.toJsonStr(user.getUserInfo())
);
return token;
}
生成内部token
@ApiOperation(value="验证令牌并获取用户", httpMethod="POST")
@RequestMapping(value="/generate", method=RequestMethod.POST)
public ApiResultDTO<String> generateAccessToken(@RequestBody AccessTokenUser user){
return RestAPITemplate.restapi(()->{
return tokenService.generateInnerToken(user);
});
}
public String generateInnerToken(AccessTokenUser user) {
Map<String,String> u=new HashMap<String,String>();
if(user!=null) {
u.put("user", JsonConverter.toJsonStr(user));
}
long expireTime = 3*60*1000;
user.setExpireTime(System.currentTimeMillis()+expireTime);
return JWTUtils.createToken(u, expireTime);
}
JWTUtils工具类
private static final byte[] SECRET = "XX#$%()(#*!()!KL<>?N<:{LWPW".getBytes();
static {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
}
public static String createToken(Map<String,String> claims, long maxLife) {
try {
JWT jwt=JWT.create();
jwt.setHeader(JWTHeader.ALGORITHM, "HS256");
jwt.setSigner(JWTSignerUtil.createSigner("HS256", SECRET));
if(claims!=null&&claims.size()>0){
jwt.addPayloads(claims);
}
jwt.setExpiresAt(new Date(System.currentTimeMillis()+maxLife));
String token=jwt.sign();
return token;
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("根据token获取对象失败");
}
}
public static Map<String,String> verifyTokenAndGetClaims(String jwt_token) {
JWT jwt=JWT.create();
jwt.setHeader(JWTHeader.ALGORITHM, "HS256");
jwt.setSigner(JWTSignerUtil.createSigner("HS256", SECRET));
try {
jwt.parse(jwt_token);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("校验jwt_token令牌失败");
}
if(!jwt.verify()) {
throw new RuntimeException("无效的jwt_token");
}
Map<String,String> result = new HashMap<String,String>();
JSONObject jo=jwt.getPayloads();
if(jo==null||jo.size()==0) {
return result;
}
for (Map.Entry<String,Object> d: jo.entrySet()) {
if(d.getValue()==null) {
continue;
}
result.put(d.getKey(), d.getValue().toString());
}
return result;
}
}
退出接口
@AuditLogEnregistor(module=AuditLogEvent.MODULE_LOGON,action="退出",before=true)
@ApiOperation(value="用户退出", httpMethod="GET")
@RequestMapping(value="/api/logout",method=RequestMethod.GET)
public ApiResultDTO<String> logout(HttpServletRequest hreq,HttpServletResponse hrep){
return RestAPITemplate.restapi(new IMyLogic<String>() {
@Override
public String logic() {
AccessTokenUser loginUser = new AccessTokenUserAssembler().getAccessTokenUserFromReq(hreq);
if(loginUser!=null){
tokenService.removeAccessToken(loginUser.getAccessToken());
}
LoginInitUtils.afterLogout(true, hreq, hrep);
return null;
}
});
}
}
@ApiOperation(value="根据信息生成访问令牌", httpMethod="POST")
@ApiImplicitParam(name="access_token",value="令牌",required=true)
@RequestMapping(value="/remove", method=RequestMethod.POST)
public ApiResultDTO<String> removeAccessToken(@RequestParam("access_token")String access_token){
return RestAPITemplate.restapi(()->{
tokenService.removeAccessToken(access_token);
return null;
});
}
public void removeAccessToken(String tokenId) {
Token token=tokenRepository.findTokenByTokenId(tokenId);
if(token!=null) {
tokenRepository.remove(token);
}
List<ServiceInstance> serviceInstances = discoveryClient.getInstances(serviceName);
if(CollectionUtils.isEmpty(serviceInstances)) {
serviceInstances = discoveryClient.getInstances(StringUtils.upperCase(serviceName));
}
if(serviceInstances.size()<=1) {
getCache().invalidate(tokenId);
return;
}
List<Future<String>> futures=new ArrayList<Future<String>>();
ExecutorService executor=Executors.newCachedThreadPool();
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<List<String>> req = new HttpEntity<>(Arrays.asList(tokenId), httpHeaders);
for (ServiceInstance serviceInstance : serviceInstances) {
futures.add(executor.submit(()->{
try {
restTemplate.postForObject(serviceInstance.getUri()+"/"+serviceName+"/access_token/remove/notice",
req, ApiResultDTO.class);
return "success";
} catch (Exception e) {
return "failed";
}
}));
}
executor.shutdown();
for (Future<String> future : futures) {
try {
future.get(5,TimeUnit.SECONDS);
} catch (Exception e) {
}
}
executor.shutdownNow();
}
token模型
@Entity
@Table(
name = "T_TOKEN",
indexes = {
@Index(name="t_token_idx1",columnList="user_id"),
@Index(name="t_token_idx2",columnList="fromType"),
@Index(name="t_token_idx3",columnList="expireTime"),
@Index(name="t_token_idx4",columnList="tokenSgin",unique=true),
@Index(name="t_token_idx5",columnList="deviceId")
}
)
public class Token {
@Id
@Column(length=200)
private String id;
private int expireMinutes=-1;
@Basic(optional=true)
private Date expireTime;
@Basic(optional=false)
private Date lastUpdateDate;
@AttributeOverrides({
@AttributeOverride(name="id",column=@Column(name="user_id",length=100,nullable=false)),
@AttributeOverride(name="name",column=@Column(name="user_name",length=100,nullable=false))
})
private SysDataSimpleValObj user;
@Column(length=100)
private String account;
@Basic(optional=false)
@Column(length=20)
private String fromType;
@Column(length=200)
private String deviceId;
@Column(length=200)
private String tokenSgin;
@Lob
@Type(type="org.hibernate.type.TextType")
private String otherUserInfo;
public Token() {
super();
this.lastUpdateDate = new Date();
}
private Token(String tokenId, int expireMinutes, SysDataSimpleValObj user,
String account, String fromType, String deviceId, String tokenSgin,
String otherUserInfo
) {
this();
this.id = tokenId;
this.expireMinutes = expireMinutes;
this.expireTime = CalendarUtils.offsetMinutes(new Date(), expireMinutes);
this.user = user;
this.fromType = fromType;
this.deviceId = deviceId;
this.tokenSgin = tokenSgin;
this.otherUserInfo = otherUserInfo;
}
public static Token buildToken(String fromType, int expireMinutes, AccessTokenUser user,
String account, String deviceId) {
String tokenId = Utils.getUUID("");
if(StringUtils.isNotBlank(user.getAccessToken())) {
tokenId=user.getAccessToken();
}
String tokenSgin = user.getUser().getId()+"_"+fromType;
if(!XysdConstants.TOKEN_FORMTYPE_APP.equals(fromType)
||!XysdConstants.TOKEN_FORMTYPE_WECHATAPPLET.equals(fromType)
){
tokenSgin+="_"+Utils.getUUID("");
}
Token token = new Token(tokenId, expireMinutes, user.getUser(), account,
fromType, deviceId, tokenSgin, JsonConverter.toJsonStr(user.getUserInfo())
);
return token;
}
public boolean isValidTokenNow(){
return this.getExpireTime()==null || this.getExpireTime().getTime()>new Date().getTime();
}
public void refreshExpireTime() {
if(this.expireTime==null||this.expireMinutes<0) {
return ;
}
this.expireTime = CalendarUtils.offsetMinutes(this.expireTime, this.expireMinutes);
}
}