package com.beifan.foxlibc.modules.controller; import com.beifan.foxlibc.framework.utils.Assert; import com.beifan.foxlibc.framework.utils.JvmCache; import com.beifan.foxlibc.framework.utils.R; import com.beifan.foxlibc.framework.utils.StringUtils; import com.beifan.foxlibc.framework.utils.generator.UniqueUtils; import com.beifan.foxlibc.framework.utils.generator.VerifyCodes; import com.beifan.foxlibc.framework.utils.security.DoubleCoder; import com.beifan.foxlibc.framework.utils.time.DateUtils; import com.beifan.foxlibc.framework.utils.time.TimeUnits; import com.beifan.foxlibc.modules.configuration.NV_TOKEN; import com.beifan.foxlibc.modules.pojo.model.SystemSet; import com.beifan.foxlibc.modules.pojo.model.User; import com.beifan.foxlibc.modules.pojo.model.UserLogininfo; import com.beifan.foxlibc.modules.pojo.webio.MakeVerificationCodeIn; import com.beifan.foxlibc.modules.pojo.webio.ResetPasswdIn; import com.beifan.foxlibc.modules.pojo.webio.ResetSecPasswdIn; import com.beifan.foxlibc.modules.pojo.webio.io.AuthenticationCodeIn; import com.beifan.foxlibc.modules.pojo.webio.io.AuthenticationOut; import com.beifan.foxlibc.modules.pojo.webio.io.AuthenticationSecUserIn; import com.beifan.foxlibc.modules.pojo.webio.io.AuthenticationUserIn; import com.beifan.foxlibc.modules.service.UserOperateService; import com.beifan.foxlibc.modules.service.UserService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RestController; import javax.validation.Valid; import java.util.Date; import java.util.Map; import java.util.Objects; import java.util.regex.Pattern; /** * 认证服务:登录/code生成/解析/验证 * * @author lts */ @RestController @Slf4j public class AuthenticationController { @Autowired private UserService userService; @Autowired private UserOperateService userOperateService; // @Value("${foxlibc.debug}") private boolean __DEBUG__; @Value("${foxlibc.mlogin:false}") private boolean __M_LOGIN__; /** * 缓存 */ private final JvmCache jvmCache = new JvmCache(); /* 密码校验规则 */ static String PWD_VALID_RGX = "^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[`!@#$%^&*()_+{}\":?><,./';\\[\\]=-\\\\|])(?=\\S+$).{8,20}$"; /** * 生成appToken */ @NV_TOKEN @PostMapping("/application-token") public R applicationToken(@Valid @RequestBody AuthenticationCodeIn authenticationCodeIn) { Assert.throwIfBool(userService.application(authenticationCodeIn.getAppid(), authenticationCodeIn.getAppSecret()), "appid或app secret不正确"); // authenticationService.useEnv(JwtConst.APP); // String appToken = authenticationService.createToken(Maps.ofMap(JwtConst.APP, authenticationCodeIn.getAppid())); String appToken = UniqueUtils.uuid().toLowerCase(); Date expireTime = DateUtils.plus(new Date(), 1, TimeUnits.DAYS); /* 记录登录信息 */ // Date expireTime = authenticationService.getExpireTime(appToken); String appid = authenticationCodeIn.getAppid(); UserLogininfo userLogininfo = new UserLogininfo(appid, null, expireTime, appToken, null, null, null); userService.saveUserLogininfo(userLogininfo); SystemSet systemSet = userService.systemSetting(); Boolean verificationCode = systemSet.getVerificationCode(); return R.ok( "appToken", appToken, "expireTime", DateUtils.format(expireTime), "verificationCode", verificationCode ); } /** * 验证token */ @NV_TOKEN @PostMapping("/valid/token") public R validToken(@RequestHeader Map headers) { String token = headers.get("authorization"); String authId = headers.get("authid"); /* WRAN: 异常信息中的 -C401 是指定返回Code。 如果删除了前端将不能通过 401 判断用户是否过期 */ // Assert.throwIfBool(!jvmCache.contains(CacheConst.TOKEN_BLACKLIST(code)), "-C401 TOKEN已被禁用"); /* 如果验证失败,抛出异常 */ // Assert.throwIfBool(authenticationService.validate(code), "-C401 TOKEN已过期"); UserLogininfo userLogininfo = userService.queryUserLogininfo(token); Date expireTime = userLogininfo.getExpireTime(); Assert.throwIfBool(!DateUtils.gteq(new Date(), expireTime), "-C401 TOKEN已过期"); if (Objects.nonNull(authId)) { userOperateService.operateLog(userLogininfo.getUserId(), authId); } return R.ok(); } /** * 生成验证码 */ @NV_TOKEN @PostMapping("/verification-code") public R verificationCode(@Valid @RequestBody MakeVerificationCodeIn makeVerificationCodeIn) { String code; SystemSet systemSet = userService.systemSetting(); int vlen = Integer.parseInt(systemSet.getVerificationCodeLength()); /* true 只有数字,false 数字加字符 */ if (SystemSet.ONLY_NUMBER.equals(systemSet.getVerificationCodeType())) { code = VerifyCodes.randomSimpleVerifyCode(vlen); } else { code = VerifyCodes.randomComplexVerifyCode(vlen); } Date codeExpireTime = DateUtils.plus(new Date(), 60, TimeUnits.SECONDS); // jvmCache.set(CacheConst.VERIFICATION_CODE(makeVerificationCodeIn.getAppToken()), code, (60 * 3)); String appToken = makeVerificationCodeIn.getAppToken(); UserLogininfo userLogininfo = userService.queryUserLogininfo(appToken); userLogininfo.setVerificationCode(code); userLogininfo.setCodeExpireTime(codeExpireTime); userService.updateUserLogininfo(userLogininfo, appToken); return R.ok(code); } /** * 用户登录 */ @NV_TOKEN @PostMapping("/sign-in") public R signin(@RequestHeader("Authorization") String appToken, @Valid @RequestBody AuthenticationUserIn authenticationUserIn) { // String sessionid = getSessionId(); String username = authenticationUserIn.getUsername(); SystemSet systemSet = userService.systemSetting(); /* 验证码校验 */ // String verificationCode = jvmCache.get(CacheConst.VERIFICATION_CODE(appToken)); UserLogininfo userLogininfo = userService.queryUserLogininfo(appToken); if (systemSet.getVerificationCode()) { String verificationCode = userLogininfo.getVerificationCode(); Assert.throwIfBool(verificationCode != null && verificationCode.equals(authenticationUserIn.getVerificationCode()), "验证码错误"); boolean isCheckFull = systemSet.getVerificationCodeAa() ? Objects.equals(verificationCode, authenticationUserIn.getVerificationCode()) : Objects.equals(verificationCode.toLowerCase(), authenticationUserIn.getVerificationCode().toLowerCase()); Assert.throwIfBool(isCheckFull, "验证码不正确"); Date codeExpireTime = userLogininfo.getCodeExpireTime(); Assert.throwIfBool(!DateUtils.gteq(new Date(), codeExpireTime), "-C401 验证码已过期"); } // 查询用户 User u = userService.queryUserByUsername(username); Assert.throwIfBool(!"0".equals(u.getUserStatus()), "当前用户状态已被禁用, 不允许登录"); String userid; try { /* 验证用户名密码是否可以登录 */ userid = userService.sign(username, authenticationUserIn.getPassword()); // 验证成功则清零失败次数 userService.updateFailCount(username, 0); } catch (Exception e) { // 验证失败 更新失败次数 if (u.getFailCount() < 4) { userService.updateFailCount(username, u.getFailCount() + 1); } else { // 超过次数则禁用用户,并且错误次数重置为0 userService.updateStatus(username, "0"); userService.updateFailCount(username, 0); } // 并且抛出异常 throw e; } /* 生成token */ // authenticationService.useEnv(JwtConst.USR); // String token = authenticationService.createToken(Maps.ofMap(JwtConst.USR_ID, userid)); // Date tokenDate = authenticationService.getExpireTime(token); String token = UniqueUtils.uuid().toLowerCase(); Date tokenDate = DateUtils.plus(new Date(), 1, TimeUnits.DAYS); UserLogininfo appLoginInfo = userService.queryUserLogininfo(appToken); appLoginInfo.setVerificationCode(null); appLoginInfo.setCodeExpireTime(null); appLoginInfo.setUserId(userid); appLoginInfo.setToken(token); appLoginInfo.setExpireTime(tokenDate); /* 如果不允许其他地方登陆的话,删除其他地方登录过的缓存 */ if (Boolean.FALSE.equals(systemSet.getAllowMultipleMachineLogins())) { userService.deleteUserLogininfoByUserId(userid); } /* 保存登录信息 */ userService.updateUserLogininfo(appLoginInfo, appToken); return R.ok( "token", token, "userid", userid, "date", tokenDate ); } /** * 刷新token */ @PostMapping("/flush-token") public R flushToken(@RequestHeader("Authorization") String token) { // String sessionid = getSessionId(); // String env = authenticationService.getEnv(token); UserLogininfo userLogininfo = userService.queryUserLogininfo(token); Date expireTime = DateUtils.plus(new Date(), 1, TimeUnits.DAYS); userLogininfo.setExpireTime(expireTime); userService.updateUserLogininfo(userLogininfo, token); return R.ok("expireTime", DateUtils.format(expireTime)); } /** * 退出登录 */ @PostMapping("/sign-out") public R signout(@RequestHeader("Authorization") String token) { /* 添加到黑名单 */ // Date expireTime = authenticationService.getExpireTime(token); // jvmCache.set(CacheConst.TOKEN_BLACKLIST(token), 0, expireTime); /* 删除登录日志中的数据 */ userService.deleteUserLogininfoByToken(token); return R.ok(); } /** * 获取系统设置 */ @PostMapping("/system/setting") public R systemSet() { return R.ok(userService.systemSetting()); } /** * 用户登录 */ @NV_TOKEN @PostMapping("/sec-sign-in") public R secsignin(@Valid @RequestBody AuthenticationSecUserIn authenticationUserIn) { String userId = authenticationUserIn.getUserId(); SystemSet systemSet = userService.systemSetting(); UserLogininfo userLogininfos = userService.queryUserLogininfosByUserIdPS(userId); if (systemSet.getVerificationCode()) { String verificationCode = userLogininfos.getVerificationCode(); Assert.throwIfBool(verificationCode != null && verificationCode.equals(authenticationUserIn.getVerificationCode()), "验证码错误"); boolean isCheckFull = systemSet.getVerificationCodeAa() ? Objects.equals(verificationCode, authenticationUserIn.getVerificationCode()) : Objects.equals(verificationCode.toLowerCase(), authenticationUserIn.getVerificationCode().toLowerCase()); Assert.throwIfBool(isCheckFull, "验证码不正确"); Date codeExpireTime = userLogininfos.getCodeExpireTime(); Assert.throwIfBool(!DateUtils.gteq(new Date(), codeExpireTime), "-C401 验证码已过期"); } /* 验证用户名密码是否可以登录 */ String userid = userService.secsign(userId, authenticationUserIn.getPassword()); return R.ok( "userid", userid ); } // /** // * 获取系统设置 // */ // @PostMapping("/authQuery") // public R authQuery(@RequestHeader("Authorization") String token) // { // String userid = authenticationService.claimsValue(token, JwtConst.USR_ID); // return R.ok(userService.authQuery(userid)); // } // /** * 修改用户密码 */ @PostMapping("/reset-passwd") public R resetPasswd(@Valid @RequestBody ResetPasswdIn resetPasswdIn) { String userid = userService.idsign(resetPasswdIn.getUserId(), resetPasswdIn.getOriginPassword()); SystemSet systemSet = userService.systemSetting(); String pwdMatch = systemSet.getPwdCons(); /* 修改密码 */ User user = userService.userQuery(userid); user.setUserPwd(resetPasswdIn.getNewPassword()); Assert.throwIfBool(Pattern.matches(pwdMatch, user.getUserPwd()), "密码不符合规则"); String pwd = StringUtils.toLowerCase(DoubleCoder.MD5(resetPasswdIn.getNewPassword())); userService.updateUserPwd(userid, pwd); return R.ok(); } /** * 修改用户密码 */ @PostMapping("/reset-secpasswd") public R resetSecPasswd(@Valid @RequestBody ResetSecPasswdIn resetPasswdIn) { String userid = resetPasswdIn.getUserId(); /* 修改密码 */ User user = userService.userQuery(userid); user.setUserPwd(resetPasswdIn.getNewPassword()); SystemSet systemSet = userService.systemSetting(); String pwdMatch = systemSet.getPwdCons(); Assert.throwIfBool(Pattern.matches(pwdMatch, user.getUserPwd()), "密码不符合规则"); String pwd = StringUtils.toLowerCase(DoubleCoder.MD5(resetPasswdIn.getNewPassword())); userService.updateUserSecPwd(userid, pwd); return R.ok(); } // private String getSessionId() { return WebRequests.getHttpServletRequest().getSession().getId(); } }