package com.artfess.base.controller;
import com.artfess.base.annotation.ApiGroup;
import com.artfess.base.cache.annotation.CacheEvict;
import com.artfess.base.cache.annotation.CachePut;
import com.artfess.base.cache.annotation.Cacheable;
import com.artfess.base.cache.annotation.FirstCache;
import com.artfess.base.conf.JwtConfig;
import com.artfess.base.conf.SaaSConfig;
import com.artfess.base.conf.SsoConfig;
import com.artfess.base.constants.ApiGroupConsts;
import com.artfess.base.constants.CacheKeyConst;
import com.artfess.base.constants.SystemConstants;
import com.artfess.base.exception.CertificateException;
import com.artfess.base.exception.ServerRejectException;
import com.artfess.base.feign.ApplicationFeignService;
import com.artfess.base.feign.UCFeignService;
import com.artfess.base.jwt.JwtAuthenticationRequest;
import com.artfess.base.jwt.JwtAuthenticationResponse;
import com.artfess.base.jwt.JwtTokenHandler;
import com.artfess.base.model.CommonResult;
import com.artfess.base.service.LoginLogService;
import com.artfess.base.service.LoginUserService;
import com.artfess.base.service.PwdStrategyService;
import com.artfess.base.service.SecurityMachinePersonService;
import com.artfess.base.util.AppUtil;
import com.artfess.base.util.Base64;
import com.artfess.base.util.BeanUtils;
import com.artfess.base.util.EncryptUtil;
import com.artfess.base.util.FluentUtil;
import com.artfess.base.util.HttpUtil;
import com.artfess.base.util.IPUtils;
import com.artfess.base.util.JsonUtil;
import com.artfess.base.util.MapUtil;
import com.artfess.base.util.StringUtil;
import com.artfess.base.util.XmlUtil;
import com.artfess.uc.api.model.IUser;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.entity.ContentType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
@RestController
@Api(tags = "认证接口")
@ApiGroup(group = {ApiGroupConsts.GROUP_BPM, ApiGroupConsts.GROUP_FORM, ApiGroupConsts.GROUP_SYSTEM, ApiGroupConsts.GROUP_UC})
public class AuthenticationRestController {
private static final Logger logger = LoggerFactory.getLogger(AuthenticationRestController.class);
@Resource
AuthenticationManager authenticationManager;
@Resource
JwtTokenHandler jwtTokenHandler;
@Resource
UserDetailsService userDetailsService;
@Resource
SsoConfig ssoConfig;
@Value("${system.mode.demo:false}")
protected boolean demoMode;
@Resource
UCFeignService uCFeignService;
@Resource
ApplicationFeignService applicationFeignService;
@Resource
LoginLogService loginLogService;
@Resource
LoginUserService loginUserService;
@Resource
SaaSConfig saasConfig;
@Resource
JwtConfig jwtConfig;
/**
* 删除缓存的用户详情
*
该方法没有方法体,通过注解在切面中删除缓存数据
*
* @param userAccount
*/
private void deleteUserDetailsCache(String userAccount) {
AuthenticationRestController bean = AppUtil.getBean(getClass());
bean.delUserDetailsCache(userAccount);
bean.delUsernamesCache(userAccount);
}
@CacheEvict(value = CacheKeyConst.EIP_UC_USER_ACCOUNT, key = "#userAccount")
protected void delUserDetailsCache(String userAccount) {
}
@CacheEvict(value = CacheKeyConst.EIP_UC_USER_NAME, key = "#userAccount")
protected void delUsernamesCache(String userAccount) {
}
@CachePut(value = CacheKeyConst.EIP_UC_USER_LOGIN_ERROR_NUM, key = "#account+'_'+#tenantId",
firstCache = @FirstCache(expireTime = 300, timeUnit = TimeUnit.SECONDS))
protected Integer putLoginErrorNumInCache(String tenantId, String account, Integer errorNum) {
return errorNum;
}
@Cacheable(value = CacheKeyConst.EIP_UC_USER_LOGIN_ERROR_NUM, key = "#account+'_'+#tenantId")
protected Integer getUserLoginErrorNumCache(String tenantId, String account) {
return null;
}
@CacheEvict(value = CacheKeyConst.EIP_UC_USER_LOGIN_ERROR_NUM, key = "#account+'_'+#tenantId")
protected void delUserLoginErrorNumCache(String tenantId,String account) {
}
@RequestMapping(value = "/auth", method = RequestMethod.POST, produces = {"application/json; charset=utf-8"})
@ApiOperation(value = "登录系统", httpMethod = "POST", notes = "登录系统")
public ResponseEntity> createAuthenticationToken(@RequestBody JwtAuthenticationRequest authenticationRequest) throws AuthenticationException, CertificateException {
//判断版本授权信息
/* JsonNode jsonNode = uCFeignService.checkSysAuthorization();
String resultCode = jsonNode.get("message").asText();
if(resultCode == null ||(!resultCode.split(":")[0].equals("SUCCESS") && !resultCode.split(":")[0].equals("NO_DECRYPT_WILL_DATE_LONG"))){
throw new RuntimeException(resultCode.split(":")[1]);
}*/
String reqAccount = authenticationRequest.getUsername();
String reqPassword = "";
//清除用户缓存
this.deleteUserDetailsCache(reqAccount);
String errorMsg = "";
final UserDetails userDetails = userDetailsService.loadUserByUsername(authenticationRequest.getUsername());
PwdStrategyService service = AppUtil.getBean(PwdStrategyService.class);
JsonNode json = service.getJsonDefault();
AuthenticationRestController bean = AppUtil.getBean(getClass());
if (BeanUtils.isNotEmpty(json)) {
long autoUnlockTime = 5l;
if(BeanUtils.isNotEmpty(json.get("autoUnlockTime"))){
autoUnlockTime = json.get("autoUnlockTime").asLong();
}
if (userDetails instanceof IUser) {
IUser user = ((IUser) userDetails);
if (2 == user.getLockedStatus()) {
LocalDateTime lockedTime = user.getLockedTime();
Long intervalMinutes = (LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli() - lockedTime.toInstant(ZoneOffset.of("+8")).toEpochMilli()) / 60000l;
if (intervalMinutes >= autoUnlockTime) {
loginUserService.lockedUser(user.getAccount(), 1);
if(BeanUtils.isNotEmpty(user)){
bean.delUserLoginErrorNumCache(user.getTenantId(),reqAccount);
}else {
bean.delUserLoginErrorNumCache("-1",reqAccount);
}
} else {
long sy = autoUnlockTime - intervalMinutes;
throw new RuntimeException("账号在锁定状态中,请于【" + sy + "分钟】后登录,或联系管理员解锁!");
}
}
}
}
try {
//密码Base64解密
reqPassword = Base64.getFromBase64(authenticationRequest.getPassword());
authenticate(reqAccount, reqPassword);
} catch (Exception e) {
logger.error(String.format("Login failed account[%s].", reqAccount), e);
errorMsg = "账号或密码错误";
if (BeanUtils.isNotEmpty(e.getCause()) && e.getCause() instanceof CertificateException) {
CertificateException ce = (CertificateException) e.getCause();
errorMsg = ce.getMessage();
}
if (e instanceof LockedException) {
errorMsg = "账号被禁用或离职";
}
}
HttpServletRequest request = HttpUtil.getRequest();
HttpSession session = request.getSession();
String IP = IPUtils.getIpAddr(request);
//验证密码输入错误次数
if (StringUtil.isNotEmpty(errorMsg)) {
if (errorMsg.equals("账号或密码错误") && !isAdmin(reqAccount)) {
IUser user = null;
if (userDetails instanceof IUser) {
user = ((IUser) userDetails);
}
//获取密码策略
if (BeanUtils.isNotEmpty(json)) {
// 密码错误锁定状态(0:关闭【默认】 1:开启 )
int lockStatus = json.get("lockStatus").asInt();
// 密码错误次数
int lockTimes = json.get("lockTimes").asInt();
// 启用策略 0:停用,1:启用
int enable = json.get("enable").asInt();
if (enable == 1 && lockStatus == 1) {
Integer loginTimes = 0;
if(BeanUtils.isNotEmpty(user)){
loginTimes =bean.getUserLoginErrorNumCache(user.getTenantId(),reqAccount);
}else {
loginTimes =bean.getUserLoginErrorNumCache("-1",reqAccount);
}
// Integer loginTimes = (Integer) session.getAttribute("_loginTime_");
if (loginTimes == null) {
loginTimes = new Integer(1);
}
// session.setAttribute("_loginTime_", loginTimes = Integer.valueOf(loginTimes.intValue() + 1));
if(BeanUtils.isNotEmpty(user)){
bean.putLoginErrorNumInCache(user.getTenantId(),reqAccount,Integer.valueOf(loginTimes.intValue() + 1));
}else {
bean.putLoginErrorNumInCache("-1",reqAccount, Integer.valueOf(loginTimes.intValue() + 1));
}
String loginMsg = ",还剩:"+(lockTimes -loginTimes) +"次!";;
if (loginTimes >= lockTimes) {
loginUserService.lockedUser(reqAccount, 2);
if(loginTimes == lockTimes){
loginMsg = ",账号已锁定,请联系管理员!";
}
if(BeanUtils.isNotEmpty(user)){
bean.delUserLoginErrorNumCache(user.getTenantId(),reqAccount);
}else {
bean.delUserLoginErrorNumCache("-1",reqAccount);
}
}
errorMsg ="账号或密码错误次数:"+loginTimes+"次"+loginMsg;
throw new RuntimeException(errorMsg);
}
}
}
throw new RuntimeException(errorMsg);
}
//判断登录用户有没有绑定涉密计算机
if (!isAdmin(reqAccount)) {
SecurityMachinePersonService machineService = AppUtil.getBean(SecurityMachinePersonService.class);
if (machineService != null) {
List machineIdList = machineService.queryPersonLimitByAccount(reqAccount);
if (machineIdList != null && machineIdList.size() > 0) {
boolean allowded = false;
List ipList = machineService.queryMachineIps(machineIdList);
if (ipList != null && ipList.size() > 0) {
for (String ip : ipList) {
if (ip.equals(IP)) {
allowded = true;
break;
}
}
}
if (!allowded) {
throw new RuntimeException("用户【" + reqAccount + "】已绑定涉密机器,不能在当前机器上登录!");
}
}
}
}
// 当前切中的方法
boolean isMobile = HttpUtil.isMobile(request);
// Reload password post-security so we can generate the token
final String token = jwtTokenHandler.generateToken(userDetails);
String userName = userDetails.getUsername();
String account = "";
String userId = "";
boolean loginStatus = true;
Map userAttrs = new HashMap();
if (userDetails instanceof IUser) {
IUser user = ((IUser) userDetails);
userName = user.getFullname();
account = user.getAccount();
userId = user.getUserId();
request.setAttribute("loginUser", String.format("%s[%s]", userName, account));
//校验密码策略是否需要修改密码
loginStatus = checkUser(user, reqPassword);
userAttrs.put("tenantId", user.getTenantId());
}
//处理单用户登录
handleSingleLogin(isMobile, MapUtil.getString(userAttrs, "tenantId"), account, token);
//删除登录错误次数
// session.removeAttribute("_loginTime_");
bean.delUserLoginErrorNumCache(MapUtil.getString(userAttrs, "tenantId"), account);
//修改登录时间
loginUserService.updateLastLoginTime(account);
//loginUserService.updateUserIp();
return ResponseEntity.ok(new JwtAuthenticationResponse(token, userName, account, userId, jwtConfig.getExpirationLong(), loginStatus, userAttrs));
}
private boolean isAdmin(String account) {
String tmp = SystemConstants.SYSTEM_ACCOUNT;
String[] split = tmp.split(",");
for (String _account : split) {
if (_account.equals(account)) {
return true;
}
}
return false;
}
// cas验证ticket并获取当前登录用户账号
private String getUserNameWithCas(String ticket, String service) throws IOException {
String casUserDetail = "";
String username = null, errorCode = "";
try {
casUserDetail = FluentUtil.get(String.format("%s/p3/serviceValidate?ticket=%s&service=%s", ssoConfig.getCasUrl(), ticket, service), "");
String json = XmlUtil.toJson(casUserDetail);
JsonNode jsonNode = JsonUtil.toJsonNode(json);
if (jsonNode.has("authenticationSuccess")) {
username = jsonNode.get("authenticationSuccess").get("user").asText();
} else if (jsonNode.has("authenticationFailure")) {
errorCode = jsonNode.get("authenticationFailure").get("code").asText();
throw new RuntimeException(errorCode);
}
} catch (Exception e) {
e.printStackTrace();
logger.info("获取cas认证信息失败:" + casUserDetail);
throw new RuntimeException("获取cas认证信息失败: " + e.getMessage());
}
return username;
}
// oauth验证code并获取当前登录用户账号
private String getUserNameWithOauth(String code, String service) {
String userName = null;
String oauthTokenUrl = ssoConfig.getOauthTokenUrl();
String stufix = String.format("&code=%s&redirect_uri=%s", code, service);
try {
String header = ssoConfig.getOauthBasicHeader();
String tokenResult = FluentUtil.post(oauthTokenUrl + stufix, header, null, ContentType.APPLICATION_FORM_URLENCODED);
JsonNode jsonNode = JsonUtil.toJsonNode(tokenResult);
if (jsonNode != null && jsonNode.isObject()) {
String token = jsonNode.get(ssoConfig.getOauthAccesstokenKey()).asText();
String oauthCheckUrl = ssoConfig.getOauthCheckUrl();
String checkResult = FluentUtil.post(oauthCheckUrl + token, null, null, ContentType.APPLICATION_FORM_URLENCODED);
JsonNode checkJNode = JsonUtil.toJsonNode(checkResult);
if (checkJNode != null && checkJNode.isObject()) {
userName = checkJNode.get(ssoConfig.getOauthUsernameKey()).asText();
}
}
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("获取oauth认证信息失败", e);
}
return userName;
}
@RequestMapping(value = "/sso/auth", method = RequestMethod.GET, produces = {"application/json; charset=utf-8"})
@ApiOperation(value = "登录系统-单点登录", httpMethod = "GET", notes = "登录系统-单点登录")
public ResponseEntity> ssoAuth(@RequestParam Optional ticket, @RequestParam Optional code, @RequestParam Optional ssoMode, @RequestParam String service) throws AuthenticationException, ClientProtocolException, IOException {
Assert.isTrue(ssoConfig.isEnable(), "当前服务未开启单点登录");
String username = null;
String mode = ssoConfig.getMode();
if (ssoMode.isPresent()) {
mode = ssoMode.get();
}
// 使用cas认证
if (ticket.isPresent() && SsoConfig.MODE_CAS.equals(mode)) {
username = getUserNameWithCas(ticket.get(), service);
}
// 使用oauth认证
else if (code.isPresent() && SsoConfig.MODE_OAUTH.equals(mode)) {
username = getUserNameWithOauth(code.get(), service);
}
// 使用jwt认证
else if (code.isPresent() && SsoConfig.MODE_JWT.equals(mode)) {
username = jwtTokenHandler.getUsernameFromToken(ticket.get());
} else {
throw new ServerRejectException("单点登录模式匹配异常");
}
//清除用户缓存
this.deleteUserDetailsCache(username);
// 当前切中的方法
HttpServletRequest request = HttpUtil.getRequest();
boolean isMobile = HttpUtil.isMobile(request);
// Reload password post-security so we can generate the token
final UserDetails userDetails = userDetailsService.loadUserByUsername(username);
final String token = jwtTokenHandler.generateToken(userDetails);
String userName = userDetails.getUsername();
String account = "";
String userId = "";
Map userAttrs = new HashMap();
if (userDetails instanceof IUser) {
IUser user = ((IUser) userDetails);
userName = user.getFullname();
account = user.getAccount();
userId = user.getUserId();
request.setAttribute("loginUser", String.format("%s[%s]", userName, account));
userAttrs.put("tenantId", user.getTenantId());
}
//获取超时时间
logger.debug("通过单点认证登录成功。");
//处理单用户登录
if (!(code.isPresent() && SsoConfig.MODE_JWT.equals(mode))) {
handleSingleLogin(isMobile, MapUtil.getString(userAttrs, "tenantId"), account, token);
}
// Return the token
return ResponseEntity.ok(new JwtAuthenticationResponse(token, userName, account, userId, jwtConfig.getExpirationLong(), userAttrs));
}
@RequestMapping(value = "/sso/weixin", method = RequestMethod.GET, produces = {"application/json; charset=utf-8"})
@ApiOperation(value = "企业微信应用进入手机端-单点登录", httpMethod = "GET", notes = "企业微信应用进入手机端-单点登录")
public ResponseEntity> ssoWeixin(@RequestParam Optional code) throws AuthenticationException, ClientProtocolException, IOException {
String resultJson = HttpUtil.sendHttpsRequest(applicationFeignService.getUserInfoUrl("weChatWork", code.get()), "", "POST");
logger.error("企业微信登录返回结果:" + resultJson);
ObjectNode result = null;
try {
result = (ObjectNode) JsonUtil.toJsonNode(resultJson);
} catch (Exception e) {
logger.error(e.getMessage());
}
String errcode = result.get("errcode").asText();
if ("0".equals(errcode)) {
String wxWorkId = result.get("UserId").asText();
JsonNode simpleUser = uCFeignService.getUserByWxWorkId(wxWorkId);
if (BeanUtils.isEmpty(simpleUser) || simpleUser.isNull()) {
throw new RuntimeException("查无与您企微账号[userid:" + wxWorkId + "]绑定的eip账号");
}
String account = simpleUser.get("account").asText();
try {
//清除用户缓存
this.deleteUserDetailsCache(account);
// 当前切中的方法
HttpServletRequest request = HttpUtil.getRequest();
boolean isMobile = HttpUtil.isMobile(request);
// Reload password post-security so we can generate the token
final UserDetails userDetails = userDetailsService.loadUserByUsername(account);
final String token = jwtTokenHandler.generateToken(userDetails);
String userName = userDetails.getUsername();
String userId = "";
String tenantId = "";
if (userDetails instanceof IUser) {
IUser user = ((IUser) userDetails);
userName = user.getFullname();
userId = user.getUserId();
tenantId = user.getTenantId();
request.setAttribute("loginUser", String.format("%s[%s]", userName, account));
}
logger.debug("通过单点认证登录成功。");
//处理单用户登录
handleSingleLogin(isMobile, tenantId, account, token);
// Return the token
return ResponseEntity.ok(new JwtAuthenticationResponse(token, userName, account, userId));
} catch (Exception e) {
throw new RuntimeException("企业微信登录失败 ,eip用户账号:" + account);
}
}
throw new RuntimeException("企业微信登录失败 : " + result.get("errmsg").asText());
}
@RequestMapping(value = "/sso/weixinPublic", method = RequestMethod.GET, produces = {"application/json; charset=utf-8"})
@ApiOperation(value = "微信公众号进入手机端", httpMethod = "GET", notes = "微信公众号进入手机端")
public ResponseEntity> weixinPublic(@RequestParam Optional code) throws AuthenticationException, ClientProtocolException, IOException {
String resultJson = HttpUtil.sendHttpsRequest(applicationFeignService.getUserInfoUrl("weChatOffAcc", code.get()), "", "POST");
ObjectNode result = null;
try {
result = (ObjectNode) JsonUtil.toJsonNode(resultJson);
} catch (Exception e) {
logger.error(e.getMessage());
}
if (result.has("openid")) {
String openid = result.get("openid").asText();
CommonResult r = uCFeignService.getUserByOpenId(openid);
if (r.getState()) {
JsonNode node = r.getValue();
if (StringUtil.isNotEmpty(openid) && BeanUtils.isEmpty(node)) {
return ResponseEntity.ok(new JwtAuthenticationResponse(openid));
}
String account = node.get("account").asText();
//清除用户缓存
this.deleteUserDetailsCache(account);
// 当前切中的方法
HttpServletRequest request = HttpUtil.getRequest();
boolean isMobile = HttpUtil.isMobile(request);
// Reload password post-security so we can generate the token
final UserDetails userDetails = userDetailsService.loadUserByUsername(account);
final String token = jwtTokenHandler.generateToken(userDetails);
String userName = userDetails.getUsername();
String userId = "";
String tenantId = "";
if (userDetails instanceof IUser) {
IUser user = ((IUser) userDetails);
userName = user.getFullname();
userId = user.getUserId();
tenantId = user.getTenantId();
request.setAttribute("loginUser", String.format("%s[%s]", userName, account));
}
//处理单用户登录
handleSingleLogin(isMobile, tenantId, account, token);
// Return the token
return ResponseEntity.ok(new JwtAuthenticationResponse(token, userName, account, userId));
} else {
if (StringUtil.isNotEmpty(openid)) {
return ResponseEntity.ok(new JwtAuthenticationResponse(openid));
}
}
}
throw new RuntimeException("微信登录失败 : " + result.get("errmsg").asText());
}
@RequestMapping(value = "/sso/dingTalk", method = RequestMethod.GET, produces = {"application/json; charset=utf-8"})
@ApiOperation(value = "钉钉进入手机端", httpMethod = "GET", notes = "微信公众号进入手机端")
public ResponseEntity> dingTalk(@RequestParam Optional code) throws AuthenticationException, ClientProtocolException, IOException {
String resultJson = HttpUtil.sendHttpsRequest(applicationFeignService.getUserInfoUrl("dingtalk", code.get()), "", "GET");
ObjectNode result = null;
try {
result = (ObjectNode) JsonUtil.toJsonNode(resultJson);
} catch (Exception e) {
logger.error(e.getMessage());
}
if (result.has("userid")) {
String dingtalkId = result.get("userid").asText();
JsonNode simpleUser = uCFeignService.getUserByDingtalkId(dingtalkId);
if (BeanUtils.isEmpty(simpleUser) || simpleUser.isNull()) {
throw new RuntimeException("查无与您钉钉账号[userid:" + dingtalkId + "]绑定的eip账号");
}
String account = simpleUser.get("account").asText();
final UserDetails userDetails = userDetailsService.loadUserByUsername(account);
if (BeanUtils.isNotEmpty(userDetails)) {
//清除用户缓存
this.deleteUserDetailsCache(account);
// Reload password post-security so we can generate the token
// 当前切中的方法
HttpServletRequest request = HttpUtil.getRequest();
boolean isMobile = HttpUtil.isMobile(request);
final String token = jwtTokenHandler.generateToken(userDetails);
String userName = userDetails.getUsername();
String userId = "";
String tenantId = "";
if (userDetails instanceof IUser) {
IUser user = ((IUser) userDetails);
userName = user.getFullname();
userId = user.getUserId();
tenantId = user.getTenantId();
request.setAttribute("loginUser", String.format("%s[%s]", userName, account));
}
//处理单用户登录
handleSingleLogin(isMobile, tenantId, account, token);
// Return the token
return ResponseEntity.ok(new JwtAuthenticationResponse(token, userName, account, userId));
} else {
throw new RuntimeException("钉钉登录失败!eip账号:" + account + "不存在");
}
}
throw new RuntimeException("钉钉登录失败 : " + result.get("errmsg").asText());
}
@RequestMapping(value = "/sso/ykz", method = RequestMethod.GET, produces = {"application/json; charset=utf-8"})
@ApiOperation(value = "愉快政单点登录", httpMethod = "GET", notes = "愉快政单点登录")
public ResponseEntity> dingTalkYkz(@RequestParam Optional code) throws AuthenticationException, ClientProtocolException, IOException {
//根据愉快政的临时授权码查询愉快政的登录用户信息
String resultJson = applicationFeignService.getUserInfoForYkz(code.get());
ObjectNode result = null;
try {
result = (ObjectNode) JsonUtil.toJsonNode(resultJson);
} catch (Exception e) {
logger.error(e.getMessage());
}
boolean loginStatus = true;
if (result.has("accountId")) {
// String dingtalkId = result.get("accountId").asText();
String dingtalkAccount = result.get("account").asText();
String dingtalkEmployeeCode = result.get("employeeCode").asText();
//根据愉快政的用户ID查询与平台绑定的用户信息
JsonNode simpleUser = uCFeignService.getUserByDingtalkId(dingtalkEmployeeCode);
if (BeanUtils.isEmpty(simpleUser) || simpleUser.isNull()) {
logger.info("查无与您愉快政账号【" + dingtalkAccount + "】绑定的系统账号!");
loginStatus =false;
return ResponseEntity.ok(new JwtAuthenticationResponse(dingtalkEmployeeCode,dingtalkAccount,loginStatus));
}
String account = simpleUser.get("account").asText();
final UserDetails userDetails = userDetailsService.loadUserByUsername(account);
if (BeanUtils.isNotEmpty(userDetails)) {
//清除用户缓存
this.deleteUserDetailsCache(account);
// Reload password post-security so we can generate the token
// 当前切中的方法
HttpServletRequest request = HttpUtil.getRequest();
boolean isMobile = HttpUtil.isMobile(request);
final String token = jwtTokenHandler.generateToken(userDetails);
String userName = userDetails.getUsername();
String userId = "";
String tenantId = "";
Map userAttrs = new HashMap();
if (userDetails instanceof IUser) {
IUser user = ((IUser) userDetails);
userName = user.getFullname();
userId = user.getUserId();
tenantId = user.getTenantId();
request.setAttribute("loginUser", String.format("%s[%s]", userName, account));
}
//处理单用户登录
handleSingleLogin(isMobile, tenantId, account, token);
// Return the token
//修改登录时间
loginUserService.updateLastLoginTime(account);
//loginUserService.updateUserIp();
return ResponseEntity.ok(new JwtAuthenticationResponse(token, userName, account, userId, jwtConfig.getExpirationLong(), loginStatus, userAttrs));
} else {
throw new RuntimeException("登录失败!账号:" + account + "不存在");
}
}
throw new RuntimeException("愉快政验证失败 : " + result.get("message").asText());
}
@RequestMapping(value = "/sso/ykzBand", method = RequestMethod.POST, produces = {"application/json; charset=utf-8"})
@ApiOperation(value = "愉快政绑定并登录", httpMethod = "POST", notes = "愉快政绑定并登录")
public ResponseEntity> dingTalkYkzBand(@RequestBody JwtAuthenticationRequest authenticationRequest,@RequestParam Optional ykzEmployeeCode,@RequestParam Optional ykzAccount) throws AuthenticationException{
String reqAccount = authenticationRequest.getUsername();
String reqPassword = "";
//清除用户缓存
this.deleteUserDetailsCache(reqAccount);
String errorMsg = "";
try {
//密码Base64解密
reqPassword = Base64.getFromBase64(authenticationRequest.getPassword());
authenticate(reqAccount, reqPassword);
} catch (Exception e) {
logger.error(String.format("Login failed account[%s].", reqAccount), e);
errorMsg = "账号或密码错误";
if (BeanUtils.isNotEmpty(e.getCause()) && e.getCause() instanceof CertificateException) {
CertificateException ce = (CertificateException) e.getCause();
errorMsg = ce.getMessage();
}
if (e instanceof LockedException) {
errorMsg = "账号被禁用或离职";
}
}
HttpServletRequest request = HttpUtil.getRequest();
HttpSession session = request.getSession();
String IP = IPUtils.getIpAddr(request);
//验证密码输入错误次数
if (StringUtil.isNotEmpty(errorMsg)) {
if (errorMsg.equals("账号或密码错误") && !isAdmin(reqAccount)) {
//获取密码策略
PwdStrategyService service = AppUtil.getBean(PwdStrategyService.class);
if (service != null) {
JsonNode json = service.getJsonDefault();
if (BeanUtils.isNotEmpty(json)) {
// 密码错误锁定状态(0:关闭【默认】 1:开启 )
int lockStatus = json.get("lockStatus").asInt();
// 密码错误次数
int lockTimes = json.get("lockTimes").asInt();
// 启用策略 0:停用,1:启用
int enable = json.get("enable").asInt();
if (enable == 1 && lockStatus == 1) {
Integer loginTimes = (Integer) session.getAttribute("_loginTime_");
if (loginTimes == null) {
loginTimes = new Integer(0);
}
session.setAttribute("_loginTime_", loginTimes = Integer.valueOf(loginTimes.intValue() + 1));
if (loginTimes >= lockTimes) {
loginUserService.lockedUser(reqAccount, 2);
}
}
}
}
}
throw new RuntimeException(errorMsg);
}
//判断登录用户有没有绑定涉密计算机
if (!isAdmin(reqAccount)) {
SecurityMachinePersonService machineService = AppUtil.getBean(SecurityMachinePersonService.class);
if (machineService != null) {
List machineIdList = machineService.queryPersonLimitByAccount(reqAccount);
if (machineIdList != null && machineIdList.size() > 0) {
boolean allowded = false;
List ipList = machineService.queryMachineIps(machineIdList);
if (ipList != null && ipList.size() > 0) {
for (String ip : ipList) {
if (ip.equals(IP)) {
allowded = true;
break;
}
}
}
if (!allowded) {
throw new RuntimeException("用户【" + reqAccount + "】已绑定涉密机器,不能在当前机器上登录!");
}
}
}
}
// 当前切中的方法
boolean isMobile = HttpUtil.isMobile(request);
// Reload password post-security so we can generate the token
final UserDetails userDetails = userDetailsService.loadUserByUsername(authenticationRequest.getUsername());
final String token = jwtTokenHandler.generateToken(userDetails);
String userName = userDetails.getUsername();
String account = "";
String userId = "";
boolean loginStatus = true;
Map userAttrs = new HashMap();
if (userDetails instanceof IUser) {
IUser user = ((IUser) userDetails);
userName = user.getFullname();
account = user.getAccount();
userId = user.getUserId();
JsonNode simpleUser = uCFeignService.getUserByDingtalkId(ykzEmployeeCode.get());
if(simpleUser!=null){
String bandUserAccount =simpleUser.get("account").asText();
if(!bandUserAccount.equals(userDetails.getUsername())){
throw new RuntimeException("愉快政账号【"+ykzAccount.get()+"】已绑定平台其他用户,请联系管理员!");
}
}
ObjectNode userUnite = JsonUtil.getMapper().createObjectNode();
userUnite.put("userId", userId);
userUnite.put("dingtalkId", ykzEmployeeCode.get());
uCFeignService.updateUserUnite(userUnite);
request.setAttribute("loginUser", String.format("%s[%s]", userName, account));
//校验密码策略是否需要修改密码
loginStatus = checkUser(user, reqPassword);
userAttrs.put("tenantId", user.getTenantId());
}
//处理单用户登录
handleSingleLogin(isMobile, MapUtil.getString(userAttrs, "tenantId"), account, token);
//删除登录错误次数
session.removeAttribute("_loginTime_");
//修改登录时间
loginUserService.updateLastLoginTime(account);
//loginUserService.updateUserIp();
return ResponseEntity.ok(new JwtAuthenticationResponse(token, userName, account, userId, jwtConfig.getExpirationLong(), loginStatus, userAttrs));
}
@RequestMapping(value = "/sso/ykzEmployeeCode", method = RequestMethod.POST, produces = {"application/json; charset=utf-8"})
@ApiOperation(value = "愉快政EmployeeCode登录", httpMethod = "POST", notes = "愉快政EmployeeCode登录")
public ResponseEntity> dingTalkYkzEmployeeCode(@RequestParam Optional ykzEmployeeCode) throws AuthenticationException{
if(ykzEmployeeCode==null){
throw new RuntimeException("EmployeeCode不能为空!");
}
String employeeCodeEncrypt = ykzEmployeeCode.get();
String employeeCode = "";
try {
employeeCode = EncryptUtil.decrypt(employeeCodeEncrypt, "AEKXtLARGEZENITH","AES/ECB/PKCS7Padding");
} catch (Exception e) {
throw new RuntimeException("解码错误!" ,e);
}
boolean loginStatus = true;
if (StringUtil.isNotEmpty(employeeCode)) {
//根据愉快政的用户ID查询与平台绑定的用户信息
JsonNode simpleUser = uCFeignService.getUserByYkzEmployeeCode(employeeCode);
if (BeanUtils.isEmpty(simpleUser) || simpleUser.isNull()) {
logger.info("查无与您愉快政账号绑定的系统账号!");
loginStatus =false;
throw new RuntimeException("查无与您愉快政账号绑定的系统账号!");
}
String account = simpleUser.get("account").asText();
final UserDetails userDetails = userDetailsService.loadUserByUsername(account);
if (BeanUtils.isNotEmpty(userDetails)) {
//清除用户缓存
this.deleteUserDetailsCache(account);
// Reload password post-security so we can generate the token
// 当前切中的方法
HttpServletRequest request = HttpUtil.getRequest();
boolean isMobile = HttpUtil.isMobile(request);
final String token = jwtTokenHandler.generateToken(userDetails);
String userName = userDetails.getUsername();
String userId = "";
String tenantId = "";
Map userAttrs = new HashMap();
if (userDetails instanceof IUser) {
IUser user = ((IUser) userDetails);
userName = user.getFullname();
userId = user.getUserId();
tenantId = user.getTenantId();
request.setAttribute("loginUser", String.format("%s[%s]", userName, account));
}
//处理单用户登录
handleSingleLogin(isMobile, tenantId, account, token);
// Return the token
//修改登录时间
loginUserService.updateLastLoginTime(account);
//loginUserService.updateUserIp();
return ResponseEntity.ok(new JwtAuthenticationResponse(token, userName, account, userId, jwtConfig.getExpirationLong(), loginStatus, userAttrs));
} else {
throw new RuntimeException("登录失败!账号:" + account + "不存在");
}
}
throw new RuntimeException("登录验证失败 【employeeCode】为空! " );
}
/**
* @return
* @throws IOException
*/
@RequestMapping(value = "/sso/info", method = RequestMethod.GET, produces = {"application/json; charset=utf-8"})
@ApiOperation(value = "单点登录配置", httpMethod = "GET", notes = "单点登录配置")
public ResponseEntity