AuthenticationController.java 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. package com.beifan.foxlibc.modules.controller;
  2. import com.beifan.foxlibc.framework.utils.Assert;
  3. import com.beifan.foxlibc.framework.utils.JvmCache;
  4. import com.beifan.foxlibc.framework.utils.R;
  5. import com.beifan.foxlibc.framework.utils.StringUtils;
  6. import com.beifan.foxlibc.framework.utils.generator.UniqueUtils;
  7. import com.beifan.foxlibc.framework.utils.generator.VerifyCodes;
  8. import com.beifan.foxlibc.framework.utils.security.DoubleCoder;
  9. import com.beifan.foxlibc.framework.utils.time.DateUtils;
  10. import com.beifan.foxlibc.framework.utils.time.TimeUnits;
  11. import com.beifan.foxlibc.modules.configuration.NV_TOKEN;
  12. import com.beifan.foxlibc.modules.pojo.model.SystemSet;
  13. import com.beifan.foxlibc.modules.pojo.model.User;
  14. import com.beifan.foxlibc.modules.pojo.model.UserLogininfo;
  15. import com.beifan.foxlibc.modules.pojo.webio.MakeVerificationCodeIn;
  16. import com.beifan.foxlibc.modules.pojo.webio.ResetPasswdIn;
  17. import com.beifan.foxlibc.modules.pojo.webio.ResetSecPasswdIn;
  18. import com.beifan.foxlibc.modules.pojo.webio.io.AuthenticationCodeIn;
  19. import com.beifan.foxlibc.modules.pojo.webio.io.AuthenticationOut;
  20. import com.beifan.foxlibc.modules.pojo.webio.io.AuthenticationSecUserIn;
  21. import com.beifan.foxlibc.modules.pojo.webio.io.AuthenticationUserIn;
  22. import com.beifan.foxlibc.modules.service.UserOperateService;
  23. import com.beifan.foxlibc.modules.service.UserService;
  24. import com.fasterxml.jackson.core.JsonProcessingException;
  25. import com.fasterxml.jackson.databind.ObjectMapper;
  26. import lombok.extern.slf4j.Slf4j;
  27. import org.springframework.beans.factory.annotation.Autowired;
  28. import org.springframework.beans.factory.annotation.Value;
  29. import org.springframework.web.bind.annotation.PostMapping;
  30. import org.springframework.web.bind.annotation.RequestBody;
  31. import org.springframework.web.bind.annotation.RequestHeader;
  32. import org.springframework.web.bind.annotation.RestController;
  33. import javax.validation.Valid;
  34. import java.util.Date;
  35. import java.util.Map;
  36. import java.util.Objects;
  37. import java.util.regex.Pattern;
  38. /**
  39. * 认证服务:登录/code生成/解析/验证
  40. *
  41. * @author lts
  42. */
  43. @RestController
  44. @Slf4j
  45. public class AuthenticationController {
  46. @Autowired
  47. private UserService userService;
  48. @Autowired
  49. private UserOperateService userOperateService;
  50. // @Value("${foxlibc.debug}") private boolean __DEBUG__;
  51. @Value("${foxlibc.mlogin:false}")
  52. private boolean __M_LOGIN__;
  53. /**
  54. * 缓存
  55. */
  56. private final JvmCache jvmCache = new JvmCache();
  57. /* 密码校验规则 */
  58. static String PWD_VALID_RGX =
  59. "^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[`!@#$%^&*()_+{}\":?><,./';\\[\\]=-\\\\|])(?=\\S+$).{8,20}$";
  60. /**
  61. * 生成appToken
  62. */
  63. @NV_TOKEN
  64. @PostMapping("/application-token")
  65. public R<AuthenticationOut> applicationToken(@Valid @RequestBody AuthenticationCodeIn authenticationCodeIn) {
  66. try {
  67. String string = new ObjectMapper().writeValueAsString(authenticationCodeIn);
  68. log.debug("获取token:{}", string);
  69. } catch (JsonProcessingException e) {
  70. e.printStackTrace();
  71. }
  72. Assert.throwIfBool(userService.application(authenticationCodeIn.getAppid(), authenticationCodeIn.getAppSecret()),
  73. "appid或app secret不正确");
  74. // authenticationService.useEnv(JwtConst.APP);
  75. // String appToken = authenticationService.createToken(Maps.ofMap(JwtConst.APP, authenticationCodeIn.getAppid()));
  76. String appToken = UniqueUtils.uuid().toLowerCase();
  77. Date expireTime = DateUtils.plus(new Date(), 1, TimeUnits.DAYS);
  78. /* 记录登录信息 */
  79. // Date expireTime = authenticationService.getExpireTime(appToken);
  80. String appid = authenticationCodeIn.getAppid();
  81. UserLogininfo userLogininfo = new UserLogininfo(appid, null, expireTime, appToken, null, null, null);
  82. userService.saveUserLogininfo(userLogininfo);
  83. SystemSet systemSet = userService.systemSetting();
  84. Boolean verificationCode = systemSet.getVerificationCode();
  85. return R.ok(
  86. "appToken", appToken,
  87. "expireTime", DateUtils.format(expireTime),
  88. "verificationCode", verificationCode
  89. );
  90. }
  91. /**
  92. * 验证token
  93. */
  94. @NV_TOKEN
  95. @PostMapping("/valid/token")
  96. public R<AuthenticationOut> validToken(@RequestHeader Map<String, String> headers) {
  97. String token = headers.get("authorization");
  98. if (Objects.isNull(token)) {
  99. token = headers.get("Authorization");
  100. }
  101. String authId = headers.get("authid");
  102. /* WRAN: 异常信息中的 -C401 是指定返回Code。
  103. 如果删除了前端将不能通过 401 判断用户是否过期 */
  104. // Assert.throwIfBool(!jvmCache.contains(CacheConst.TOKEN_BLACKLIST(code)), "-C401 TOKEN已被禁用");
  105. /* 如果验证失败,抛出异常 */
  106. // Assert.throwIfBool(authenticationService.validate(code), "-C401 TOKEN已过期");
  107. UserLogininfo userLogininfo = userService.queryUserLogininfo(token);
  108. Date expireTime = userLogininfo.getExpireTime();
  109. Assert.throwIfBool(!DateUtils.gteq(new Date(), expireTime), "-C401 TOKEN已过期");
  110. if (Objects.nonNull(authId)) {
  111. userOperateService.operateLog(userLogininfo.getUserId(), authId);
  112. }
  113. return R.ok();
  114. }
  115. /**
  116. * 生成验证码
  117. */
  118. @NV_TOKEN
  119. @PostMapping("/verification-code")
  120. public R<String> verificationCode(@Valid @RequestBody MakeVerificationCodeIn makeVerificationCodeIn) {
  121. String code;
  122. SystemSet systemSet = userService.systemSetting();
  123. int vlen = Integer.parseInt(systemSet.getVerificationCodeLength());
  124. /* true 只有数字,false 数字加字符 */
  125. if (SystemSet.ONLY_NUMBER.equals(systemSet.getVerificationCodeType())) {
  126. code = VerifyCodes.randomSimpleVerifyCode(vlen);
  127. } else {
  128. code = VerifyCodes.randomComplexVerifyCode(vlen);
  129. }
  130. Date codeExpireTime = DateUtils.plus(new Date(), 60, TimeUnits.SECONDS);
  131. // jvmCache.set(CacheConst.VERIFICATION_CODE(makeVerificationCodeIn.getAppToken()), code, (60 * 3));
  132. String appToken = makeVerificationCodeIn.getAppToken();
  133. UserLogininfo userLogininfo = userService.queryUserLogininfo(appToken);
  134. userLogininfo.setVerificationCode(code);
  135. userLogininfo.setCodeExpireTime(codeExpireTime);
  136. userService.updateUserLogininfo(userLogininfo, appToken);
  137. return R.ok(code);
  138. }
  139. /**
  140. * 用户登录
  141. */
  142. @NV_TOKEN
  143. @PostMapping("/sign-in")
  144. public R<AuthenticationOut> signin(@RequestHeader("Authorization") String appToken, @Valid @RequestBody AuthenticationUserIn authenticationUserIn) {
  145. // String sessionid = getSessionId();
  146. String username = authenticationUserIn.getUsername();
  147. SystemSet systemSet = userService.systemSetting();
  148. /* 验证码校验 */
  149. // String verificationCode = jvmCache.get(CacheConst.VERIFICATION_CODE(appToken));
  150. UserLogininfo userLogininfo = userService.queryUserLogininfo(appToken);
  151. if (systemSet.getVerificationCode()) {
  152. String verificationCode = userLogininfo.getVerificationCode();
  153. Assert.throwIfBool(verificationCode != null && verificationCode.equals(authenticationUserIn.getVerificationCode()), "验证码错误");
  154. boolean isCheckFull = systemSet.getVerificationCodeAa() ?
  155. Objects.equals(verificationCode, authenticationUserIn.getVerificationCode()) :
  156. Objects.equals(verificationCode.toLowerCase(), authenticationUserIn.getVerificationCode().toLowerCase());
  157. Assert.throwIfBool(isCheckFull, "验证码不正确");
  158. Date codeExpireTime = userLogininfo.getCodeExpireTime();
  159. Assert.throwIfBool(!DateUtils.gteq(new Date(), codeExpireTime), "-C401 验证码已过期");
  160. }
  161. // 查询用户
  162. User u = userService.queryUserByUsername(username);
  163. Assert.throwIfBool(!"0".equals(u.getUserStatus()), "当前用户状态已被禁用, 不允许登录");
  164. String userid;
  165. try {
  166. /* 验证用户名密码是否可以登录 */
  167. userid = userService.sign(username, authenticationUserIn.getPassword());
  168. // 验证成功则清零失败次数
  169. userService.updateFailCount(username, 0);
  170. } catch (Exception e) {
  171. // 验证失败 更新失败次数
  172. if (u.getFailCount() < 4) {
  173. userService.updateFailCount(username, u.getFailCount() + 1);
  174. } else {
  175. // 超过次数则禁用用户,并且错误次数重置为0
  176. userService.updateStatus(username, "0");
  177. userService.updateFailCount(username, 0);
  178. }
  179. // 并且抛出异常
  180. throw e;
  181. }
  182. /* 生成token */
  183. // authenticationService.useEnv(JwtConst.USR);
  184. // String token = authenticationService.createToken(Maps.ofMap(JwtConst.USR_ID, userid));
  185. // Date tokenDate = authenticationService.getExpireTime(token);
  186. String token = UniqueUtils.uuid().toLowerCase();
  187. Date tokenDate = DateUtils.plus(new Date(), 1, TimeUnits.DAYS);
  188. UserLogininfo appLoginInfo = userService.queryUserLogininfo(appToken);
  189. appLoginInfo.setVerificationCode(null);
  190. appLoginInfo.setCodeExpireTime(null);
  191. appLoginInfo.setUserId(userid);
  192. appLoginInfo.setToken(token);
  193. appLoginInfo.setExpireTime(tokenDate);
  194. /* 如果不允许其他地方登陆的话,删除其他地方登录过的缓存 */
  195. if (Boolean.FALSE.equals(systemSet.getAllowMultipleMachineLogins())) {
  196. userService.deleteUserLogininfoByUserId(userid);
  197. }
  198. /* 保存登录信息 */
  199. userService.updateUserLogininfo(appLoginInfo, appToken);
  200. return R.ok(
  201. "token", token,
  202. "userid", userid,
  203. "date", tokenDate
  204. );
  205. }
  206. /**
  207. * 刷新token
  208. */
  209. @PostMapping("/flush-token")
  210. public R<Void> flushToken(@RequestHeader("Authorization") String token) {
  211. // String sessionid = getSessionId();
  212. // String env = authenticationService.getEnv(token);
  213. UserLogininfo userLogininfo = userService.queryUserLogininfo(token);
  214. Date expireTime = DateUtils.plus(new Date(), 1, TimeUnits.DAYS);
  215. userLogininfo.setExpireTime(expireTime);
  216. userService.updateUserLogininfo(userLogininfo, token);
  217. return R.ok("expireTime", DateUtils.format(expireTime));
  218. }
  219. /**
  220. * 退出登录
  221. */
  222. @PostMapping("/sign-out")
  223. public R<Void> signout(@RequestHeader("Authorization") String token) {
  224. /* 添加到黑名单 */
  225. // Date expireTime = authenticationService.getExpireTime(token);
  226. // jvmCache.set(CacheConst.TOKEN_BLACKLIST(token), 0, expireTime);
  227. /* 删除登录日志中的数据 */
  228. userService.deleteUserLogininfoByToken(token);
  229. return R.ok();
  230. }
  231. /**
  232. * 获取系统设置
  233. */
  234. @PostMapping("/system/setting")
  235. public R<SystemSet> systemSet() {
  236. return R.ok(userService.systemSetting());
  237. }
  238. /**
  239. * 用户登录
  240. */
  241. @NV_TOKEN
  242. @PostMapping("/sec-sign-in")
  243. public R<AuthenticationOut> secsignin(@Valid @RequestBody AuthenticationSecUserIn authenticationUserIn) {
  244. String userId = authenticationUserIn.getUserId();
  245. SystemSet systemSet = userService.systemSetting();
  246. UserLogininfo userLogininfos = userService.queryUserLogininfosByUserIdPS(userId);
  247. if (systemSet.getVerificationCode()) {
  248. String verificationCode = userLogininfos.getVerificationCode();
  249. Assert.throwIfBool(verificationCode != null && verificationCode.equals(authenticationUserIn.getVerificationCode()), "验证码错误");
  250. boolean isCheckFull = systemSet.getVerificationCodeAa() ?
  251. Objects.equals(verificationCode, authenticationUserIn.getVerificationCode()) :
  252. Objects.equals(verificationCode.toLowerCase(), authenticationUserIn.getVerificationCode().toLowerCase());
  253. Assert.throwIfBool(isCheckFull, "验证码不正确");
  254. Date codeExpireTime = userLogininfos.getCodeExpireTime();
  255. Assert.throwIfBool(!DateUtils.gteq(new Date(), codeExpireTime), "-C401 验证码已过期");
  256. }
  257. /* 验证用户名密码是否可以登录 */
  258. String userid = userService.secsign(userId, authenticationUserIn.getPassword());
  259. return R.ok(
  260. "userid", userid
  261. );
  262. }
  263. // /**
  264. // * 获取系统设置
  265. // */
  266. // @PostMapping("/authQuery")
  267. // public R<SystemSet> authQuery(@RequestHeader("Authorization") String token)
  268. // {
  269. // String userid = authenticationService.claimsValue(token, JwtConst.USR_ID);
  270. // return R.ok(userService.authQuery(userid));
  271. // }
  272. //
  273. /**
  274. * 修改用户密码
  275. */
  276. @PostMapping("/reset-passwd")
  277. public R<Void> resetPasswd(@Valid @RequestBody ResetPasswdIn resetPasswdIn) {
  278. String userid = userService.idsign(resetPasswdIn.getUserId(), resetPasswdIn.getOriginPassword());
  279. SystemSet systemSet = userService.systemSetting();
  280. String pwdMatch = systemSet.getPwdCons();
  281. /* 修改密码 */
  282. User user = userService.userQuery(userid);
  283. user.setUserPwd(resetPasswdIn.getNewPassword());
  284. Assert.throwIfBool(Pattern.matches(pwdMatch, user.getUserPwd()), "密码不符合规则");
  285. String pwd = StringUtils.toLowerCase(DoubleCoder.MD5(resetPasswdIn.getNewPassword()));
  286. userService.updateUserPwd(userid, pwd);
  287. return R.ok();
  288. }
  289. /**
  290. * 修改用户密码
  291. */
  292. @PostMapping("/reset-secpasswd")
  293. public R<Void> resetSecPasswd(@Valid @RequestBody ResetSecPasswdIn resetPasswdIn) {
  294. String userid = resetPasswdIn.getUserId();
  295. /* 修改密码 */
  296. User user = userService.userQuery(userid);
  297. user.setUserPwd(resetPasswdIn.getNewPassword());
  298. SystemSet systemSet = userService.systemSetting();
  299. String pwdMatch = systemSet.getPwdCons();
  300. Assert.throwIfBool(Pattern.matches(pwdMatch, user.getUserPwd()), "密码不符合规则");
  301. String pwd = StringUtils.toLowerCase(DoubleCoder.MD5(resetPasswdIn.getNewPassword()));
  302. userService.updateUserSecPwd(userid, pwd);
  303. return R.ok();
  304. }
  305. // private String getSessionId() { return WebRequests.getHttpServletRequest().getSession().getId(); }
  306. }