BladeUserDetailsServiceImpl.java 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. /*
  2. * Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
  3. *
  4. * Redistribution and use in source and binary forms, with or without
  5. * modification, are permitted provided that the following conditions are met:
  6. *
  7. * Redistributions of source code must retain the above copyright notice,
  8. * this list of conditions and the following disclaimer.
  9. * Redistributions in binary form must reproduce the above copyright
  10. * notice, this list of conditions and the following disclaimer in the
  11. * documentation and/or other materials provided with the distribution.
  12. * Neither the name of the dreamlu.net developer nor the names of its
  13. * contributors may be used to endorse or promote products derived from
  14. * this software without specific prior written permission.
  15. * Author: Chill 庄骞 (smallchill@163.com)
  16. */
  17. package org.springblade.auth.service;
  18. import io.jsonwebtoken.Claims;
  19. import lombok.AllArgsConstructor;
  20. import lombok.SneakyThrows;
  21. import org.apache.commons.lang.StringUtils;
  22. import org.springblade.auth.constant.AuthConstant;
  23. import org.springblade.auth.utils.TokenUtil;
  24. import org.springblade.common.cache.CacheNames;
  25. import org.springblade.core.jwt.JwtUtil;
  26. import org.springblade.core.jwt.props.JwtProperties;
  27. import org.springblade.core.redis.cache.BladeRedis;
  28. import org.springblade.core.tool.api.R;
  29. import org.springblade.core.tool.utils.*;
  30. import org.springblade.system.cache.ParamCache;
  31. import org.springblade.system.entity.Tenant;
  32. import org.springblade.system.feign.ISysClient;
  33. import org.springblade.system.user.entity.User;
  34. import org.springblade.system.user.entity.UserInfo;
  35. import org.springblade.system.user.enums.UserEnum;
  36. import org.springblade.system.user.feign.IUserClient;
  37. import org.springframework.security.core.authority.AuthorityUtils;
  38. import org.springframework.security.core.userdetails.UserDetailsService;
  39. import org.springframework.security.core.userdetails.UsernameNotFoundException;
  40. import org.springframework.security.oauth2.common.exceptions.UserDeniedAuthorizationException;
  41. import org.springframework.stereotype.Service;
  42. import javax.servlet.http.HttpServletRequest;
  43. import java.time.Duration;
  44. import java.util.List;
  45. /**
  46. * 用户信息
  47. *
  48. * @author Chill
  49. */
  50. @Service
  51. @AllArgsConstructor
  52. public class BladeUserDetailsServiceImpl implements UserDetailsService {
  53. public static final Integer FAIL_COUNT = 5;
  54. public static final String FAIL_COUNT_VALUE = "account.failCount";
  55. private final IUserClient userClient;
  56. private final ISysClient sysClient;
  57. private final BladeRedis bladeRedis;
  58. private final JwtProperties jwtProperties;
  59. @Override
  60. @SneakyThrows
  61. public BladeUserDetails loadUserByUsername(String username) {
  62. HttpServletRequest request = WebUtil.getRequest();
  63. // 获取用户绑定ID
  64. String headerDept = request.getHeader(TokenUtil.DEPT_HEADER_KEY);
  65. String headerRole = request.getHeader(TokenUtil.ROLE_HEADER_KEY);
  66. // 获取租户ID
  67. String headerTenant = request.getHeader(TokenUtil.TENANT_HEADER_KEY);
  68. String paramTenant = request.getParameter(TokenUtil.TENANT_PARAM_KEY);
  69. String password = request.getParameter(TokenUtil.PASSWORD_KEY);
  70. String grantType = request.getParameter(TokenUtil.GRANT_TYPE_KEY);
  71. // 判断租户请求头
  72. if (StringUtil.isAllBlank(headerTenant, paramTenant)) {
  73. throw new UserDeniedAuthorizationException(TokenUtil.TENANT_NOT_FOUND);
  74. }
  75. // 判断令牌合法性
  76. if (!judgeRefreshToken(grantType, request)) {
  77. throw new UserDeniedAuthorizationException(TokenUtil.TOKEN_NOT_PERMISSION);
  78. }
  79. // 指定租户ID
  80. String tenantId = StringUtils.isBlank(headerTenant) ? paramTenant : headerTenant;
  81. // 判断登录是否锁定
  82. int count = getFailCount(tenantId, username);
  83. int failCount = Func.toInt(ParamCache.getValue(FAIL_COUNT_VALUE), FAIL_COUNT);
  84. if (count >= failCount) {
  85. throw new UserDeniedAuthorizationException(TokenUtil.USER_HAS_TOO_MANY_FAILS);
  86. }
  87. // 获取租户信息
  88. R<Tenant> tenant = sysClient.getTenant(tenantId);
  89. if (tenant.isSuccess()) {
  90. if (TokenUtil.judgeTenant(tenant.getData())) {
  91. throw new UserDeniedAuthorizationException(TokenUtil.USER_HAS_NO_TENANT_PERMISSION);
  92. }
  93. } else {
  94. throw new UserDeniedAuthorizationException(TokenUtil.USER_HAS_NO_TENANT);
  95. }
  96. // 获取用户类型
  97. String userType = Func.toStr(request.getHeader(TokenUtil.USER_TYPE_HEADER_KEY), TokenUtil.DEFAULT_USER_TYPE);
  98. // 远程调用返回数据
  99. R<UserInfo> result = null;
  100. // 根据不同用户类型调用对应的接口返回数据,用户可自行拓展
  101. if (userType.equals(UserEnum.WEB.getName())) {
  102. result = userClient.userInfo(tenantId, username, UserEnum.WEB.getName()); //客户端-填报
  103. } else if (userType.equals(UserEnum.APP.getName())) {
  104. result = userClient.userInfo(tenantId, username, UserEnum.APP.getName()); //APP
  105. } else if (userType.equals(UserEnum.ARCHIVES.getName())) {
  106. result = userClient.userInfo(tenantId, username, UserEnum.ARCHIVES.getName()); //档案
  107. } else if (userType.equals(UserEnum.MANAGER.getName())) {
  108. result = userClient.userInfo(tenantId, username, UserEnum.MANAGER.getName()); //后管
  109. } else if (userType.equals(UserEnum.HAC.getName())) {
  110. result = userClient.userInfo(tenantId, username, UserEnum.HAC.getName()); //后管
  111. }
  112. //TODO
  113. // 判断返回信息
  114. assert result != null;
  115. if (result.isSuccess()) {
  116. UserInfo userInfo = result.getData();
  117. User user = userInfo.getUser();
  118. // 用户不存在,但提示用户名与密码错误并锁定账号
  119. if (user == null || user.getId() == null) {
  120. setFailCount(tenantId, username, count);
  121. throw new UsernameNotFoundException(TokenUtil.USER_NOT_FOUND);
  122. }
  123. //用户存在但为封禁状态status=0,禁止登陆
  124. if (user.getStatus() == 0) {
  125. throw new UserDeniedAuthorizationException(TokenUtil.USER_STATUS_BAN);
  126. }
  127. // 用户存在但密码错误,超过次数则锁定账号
  128. if (grantType != null && !grantType.equals(TokenUtil.REFRESH_TOKEN_KEY) && !user.getPassword().equals(DigestUtil.hex(password))) {
  129. setFailCount(tenantId, username, count);
  130. throw new UsernameNotFoundException(TokenUtil.USER_NOT_FOUND);
  131. }
  132. // 用户角色不存在
  133. if (Func.isEmpty(userInfo.getRoles())) {
  134. throw new UserDeniedAuthorizationException(TokenUtil.USER_HAS_NO_ROLE);
  135. }
  136. /* 校验登陆账号权限,客户端填报、试验userType=1,app端userType=2,档案userType=3,后管userType=4 ,管控userType=5 ,征拆userType=6 */
  137. if (user.getUserType().contains(("1"))
  138. || user.getUserType().contains(("2"))
  139. || user.getUserType().contains(("3"))
  140. || user.getUserType().contains(("4"))
  141. || user.getUserType().contains(("5"))
  142. || user.getUserType().contains(("6"))) {
  143. if (!("1,2,3,4,5,6").equals(user.getUserType())) {
  144. //如果不是全部权限,那么分批校验登陆平台权限
  145. this.judgeLoginPermission(user);
  146. }
  147. } else {
  148. throw new UserDeniedAuthorizationException(TokenUtil.USER_ACCOUNT_NO_TYPE);
  149. }
  150. // 多部门情况下指定单部门
  151. if (Func.isNotEmpty(headerDept) && user.getDeptId().contains(headerDept)) {
  152. user.setDeptId(headerDept);
  153. }
  154. // 多角色情况下指定单角色
  155. if (Func.isNotEmpty(headerRole) && user.getRoleId().contains(headerRole)) {
  156. R<List<String>> roleResult = sysClient.getRoleAliases(headerRole);
  157. if (roleResult.isSuccess()) {
  158. userInfo.setRoles(roleResult.getData());
  159. }
  160. user.setRoleId(headerRole);
  161. }
  162. // 成功则清除登录错误次数
  163. delFailCount(tenantId, username);
  164. return new BladeUserDetails(user.getId(), user.getPhone(),
  165. user.getTenantId(), StringPool.EMPTY, user.getName(), user.getRealName(), user.getDeptId(), user.getPostId(), user.getRoleId(), Func.join(userInfo.getRoles()), Func.toStr(user.getAvatar(), TokenUtil.DEFAULT_AVATAR),
  166. username, AuthConstant.ENCRYPT + user.getPassword(), userInfo.getDetail(), true, true, true, true,
  167. AuthorityUtils.commaSeparatedStringToAuthorityList(Func.join(result.getData().getRoles())));
  168. } else {
  169. throw new UsernameNotFoundException(result.getMsg());
  170. }
  171. }
  172. /**
  173. * 获取账号错误次数
  174. *
  175. * @param tenantId 租户id
  176. * @param username 账号
  177. * @return int
  178. */
  179. private int getFailCount(String tenantId, String username) {
  180. return Func.toInt(bladeRedis.get(CacheNames.tenantKey(tenantId, CacheNames.USER_FAIL_KEY, username)), 0);
  181. }
  182. /**
  183. * 设置账号错误次数
  184. *
  185. * @param tenantId 租户id
  186. * @param username 账号
  187. * @param count 次数
  188. */
  189. private void setFailCount(String tenantId, String username, int count) {
  190. bladeRedis.setEx(CacheNames.tenantKey(tenantId, CacheNames.USER_FAIL_KEY, username), count + 1, Duration.ofMinutes(30));
  191. }
  192. /**
  193. * 清空账号错误次数
  194. *
  195. * @param tenantId 租户id
  196. * @param username 账号
  197. */
  198. private void delFailCount(String tenantId, String username) {
  199. bladeRedis.del(CacheNames.tenantKey(tenantId, CacheNames.USER_FAIL_KEY, username));
  200. }
  201. /**
  202. * 校验refreshToken合法性
  203. *
  204. * @param grantType 认证类型
  205. * @param request 请求
  206. */
  207. private boolean judgeRefreshToken(String grantType, HttpServletRequest request) {
  208. if (jwtProperties.getState() && jwtProperties.getSingle() && StringUtil.equals(grantType, TokenUtil.REFRESH_TOKEN_KEY)) {
  209. String refreshToken = request.getParameter(TokenUtil.REFRESH_TOKEN_KEY);
  210. Claims claims = JwtUtil.parseJWT(refreshToken);
  211. String tenantId = String.valueOf(claims.get("tenant_id"));
  212. String userId = String.valueOf(claims.get("user_id"));
  213. String token = JwtUtil.getRefreshToken(tenantId, userId, refreshToken);
  214. return StringUtil.equalsIgnoreCase(token, refreshToken);
  215. }
  216. return true;
  217. }
  218. /**
  219. * 校验登陆账号权限
  220. *
  221. * @param user user信息
  222. * @author liuyc
  223. */
  224. private void judgeLoginPermission(User user) {
  225. //获取平台信息
  226. String clientId = TokenUtil.getClientIdFromHeader();
  227. String result = "0";
  228. if (clientId != null) {
  229. switch (clientId) {
  230. case "client":
  231. result = "1"; //WEB = 客户端
  232. break;
  233. case "uni-app":
  234. result = "2"; //APP = APP端
  235. break;
  236. case "archives":
  237. result = "3"; //archives = 档案
  238. break;
  239. case "saber":
  240. result = "4"; //manger = 后管
  241. break;
  242. case "hac":
  243. result = "5"; //hac = 成本管控系统
  244. break;
  245. case "lar":
  246. result = "6"; //lar = 征拆系统
  247. break;
  248. }
  249. }
  250. if ((("1").equals(result) && user.getUserType().contains("1")) //web
  251. || (("2").equals(result) && user.getUserType().contains("2")) //app
  252. || (("3").equals(result) && user.getUserType().contains("3")) //档案
  253. || (("4").equals(result) && user.getUserType().contains("4")) //后管
  254. || (("5").equals(result)) // 成本管控系统
  255. || (("6").equals(result)) // 征拆系统
  256. ) {
  257. //放行
  258. return;
  259. }
  260. throw new UserDeniedAuthorizationException(TokenUtil.USER_ACCOUNT_NO_PERMISSION);
  261. }
  262. }