package com.artfess.base.controller; import com.artfess.base.annotation.ApiGroup; import com.artfess.base.cache.annotation.CacheEvict; 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.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.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.Autowired; 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.security.crypto.password.PasswordEncoder; 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; @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; @Autowired PasswordEncoder passwordEncoder; /** * 删除缓存的用户详情 *

该方法没有方法体,通过注解在切面中删除缓存数据

* * @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) { } @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 { // 如果是从总平台调转过来则不会携带密码 boolean isBase64 = true; if(null != authenticationRequest && StringUtil.isNotEmpty(authenticationRequest.getToken()) && StringUtil.isEmpty(authenticationRequest.getPassword())){ // String account = jwtTokenHandler.getUsernameFromToken(authenticationRequest.getToken()); String account = authenticationRequest.getUsername(); Map userInfo = uCFeignService.getDetailByAccountOrId(account); if(null == userInfo || userInfo.get("user") == null){ throw new RuntimeException("用户不存在或授权到期,请重新登录"); } // Map user = (Map) userInfo.get("user"); // String password = String.valueOf(user.get("password")); // String encodePassword = passwordEncoder.dncode(password); // authenticationRequest.setPassword(password); isBase64 = false; } //判断版本授权信息 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 = ""; try { //密码Base64解密 if(isBase64) { 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(); 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)); } 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()); } /** * @return * @throws IOException */ @RequestMapping(value = "/sso/info", method = RequestMethod.GET, produces = {"application/json; charset=utf-8"}) @ApiOperation(value = "单点登录配置", httpMethod = "GET", notes = "单点登录配置") public ResponseEntity> isUseCas(HttpServletRequest request, HttpServletResponse response) throws IOException { Map map = new HashMap(); map.put("enable", ssoConfig.isEnable()); map.put("ssoUrl", ssoConfig.getSsoUrl()); map.put("ssoLogoutUrl", ssoConfig.getSsoLogoutUrl()); return ResponseEntity.ok(map); } @RequestMapping(value = "/refresh", method = RequestMethod.GET) @ApiOperation(value = "刷新token", httpMethod = "GET", notes = "刷新token") public ResponseEntity refreshAndGetAuthenticationToken(HttpServletRequest request) { String authToken = request.getHeader(jwtConfig.getHeader()); final String token = authToken.substring(7); String tenantId = jwtTokenHandler.getTenantIdFromToken(token); String account = jwtTokenHandler.getUsernameFromToken(token); String refreshedToken = jwtTokenHandler.refreshToken(token); boolean isMobile = HttpUtil.isMobile(request); // 处理单用户登录 更新单用户登录的token handleSingleLogin(isMobile, tenantId, account, refreshedToken); return ResponseEntity.ok(new JwtAuthenticationResponse(refreshedToken, "", "", "")); } @RequestMapping(value = "/signout", method = RequestMethod.GET) @ApiOperation(value = "退出登录", httpMethod = "GET", notes = "使token的状态失效,必须设置jwt.single和jwt.stricky均为true") public CommonResult signout(HttpServletRequest request) { String authToken = request.getHeader(jwtConfig.getHeader()); final String token = authToken.substring(7); String tenantId = jwtTokenHandler.getTenantIdFromToken(token); String account = jwtTokenHandler.getUsernameFromToken(token); boolean isMobile = HttpUtil.isMobile(request); handleLogout(isMobile, tenantId, account); return new CommonResult<>("退出成功"); } /** * Authenticates the user. If something is wrong, an {@link AuthenticationException} will be thrown */ private void authenticate(String username, String password) throws AuthenticationException, CertificateException { Objects.requireNonNull(username); Objects.requireNonNull(password); authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password)); } private boolean checkUser(IUser user, String password) { if (!user.isAdmin()) { //非系统管理员 PwdStrategyService service = AppUtil.getBean(PwdStrategyService.class); if (service == null) { return true; } JsonNode json = service.getJsonDefault(); if (BeanUtils.isNotEmpty(json)) { // 初始化密码 String initPwd = json.get("initPwd").asText(); // 密码策略 int pwdRule = json.get("pwdRule").asInt(); // 密码长度 int pwdLength = json.get("pwdLength").asInt(); // 密码可用时长 int duration = json.get("duration").asInt(); // 锁定后自动解锁时长 long autoUnlockTime = json.get("autoUnlockTime").asLong(); // 首次登录是否必须修改密码(0:否 1:是) int initUpdate = json.get("initUpdate").asInt(); // 启用策略 0:停用,1:启用 int enable = json.get("enable").asInt(); 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); } else { long sy = autoUnlockTime - intervalMinutes; throw new RuntimeException("账号在锁定状态中,请于【" + sy + "分钟】后登录,或联系管理员解锁!"); } } if (enable == 1) { //密码是否默认的密码 if (password.equals(initPwd)) { return false; } //密码长度 if (password.length() < pwdLength) { return false; } //密码策略 if (pwdRule != 1) { if (pwdRule == 2) {//必须包含数字、字母 String regex = "^(?![a-zA-z]+$)(?!\\d+$)(?![!@#$%^&*]+$)[a-zA-Z\\d!@#$%^&*]+$"; boolean result = password.matches(regex); if (!result) { return false; } } else if (pwdRule == 3) {//必须包含数字、字母、特殊字符 String regex = "^(?=.*?[A-Za-z])(?=.*?\\d)(?=.*?[~!@#$%^&*()_+`\\-={}:\";'<>?,.\\/])[a-zA-Z\\d~!@#$%^&*()_+`\\-={}:\";'<>?,.\\/]*$"; boolean result = password.matches(regex); if (!result) { return false; } } else if (pwdRule == 4) {//必须包含数字、大小字母、特殊字符 String regex = "^(?=.*?[a-z])(?=.*?[A-Z])(?=.*?\\d)(?=.*?[~!@#$%^&*()_+`\\-={}:\";'<>?,.\\/])[a-zA-Z\\d~!@#$%^&*()_+`\\-={}:\";'<>?,.\\/]*$"; boolean result = password.matches(regex); if (!result) { return false; } } } //密码首次修改 if (initUpdate == 1) { if (null == user.getLastLoginTime()) { return false; } } //密码策略时间 LocalDateTime pwdCreateTime = user.getPwdCreateTime(); if (BeanUtils.isNotEmpty(pwdCreateTime)) { LocalDateTime currenTime = LocalDateTime.now(); int size = (int) (currenTime.toLocalDate().toEpochDay() - pwdCreateTime.toLocalDate().toEpochDay()); if (size > duration) { return false; } } } } } return true; } /** * 处理单用户登录 * * @param isMobile * @param username * @param token */ private void handleSingleLogin(boolean isMobile, String tenantId, String username, String token) { String userAgent = isMobile ? "mobile" : "pc"; //如果是单用户登录 if (jwtConfig.isSingle()) { // 非SaaS模式 if (StringUtil.isEmpty(tenantId) && !saasConfig.isEnable()) { tenantId = "-1"; } // 以当前登录设备、租户ID、用户账号为key将token存放到缓存中 jwtTokenHandler.putTokenInCache(userAgent, tenantId, username, jwtConfig.getExpiration(), token); }else{ HttpServletRequest request = HttpUtil.getRequest(); String IP = IPUtils.getIpAddr(request); jwtTokenHandler.putTokenInCache(userAgent, tenantId, username,IP, jwtConfig.getExpiration(), token); } //处理用户登录日志 loginLogService.log(username, isMobile ? "mobile" : "pc"); } /** * 处理用户登出 * * @param tenantId * @param account */ private void handleLogout(boolean isMobile, String tenantId, String account) { //如果是单用户登录 if (jwtConfig.isSingle()) { String userAgent = isMobile ? "mobile" : "pc"; // 非SaaS模式 if (StringUtil.isEmpty(tenantId) && !saasConfig.isEnable()) { tenantId = "-1"; } jwtTokenHandler.removeFromCache(userAgent, tenantId, account); } } }