BladeUserDetailsServiceImpl.java 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  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. if (userInfo == null){
  118. throw new UsernameNotFoundException(TokenUtil.USER_NOT_FOUND);
  119. }
  120. User user = userInfo.getUser();
  121. // 用户不存在,但提示用户名与密码错误并锁定账号
  122. if (user == null || user.getId() == null) {
  123. setFailCount(tenantId, username, count);
  124. throw new UsernameNotFoundException(TokenUtil.USER_NOT_FOUND);
  125. }
  126. //用户存在但为封禁状态status=0,禁止登陆
  127. if (user.getStatus() == 0) {
  128. throw new UserDeniedAuthorizationException(TokenUtil.USER_STATUS_BAN);
  129. }
  130. // 用户存在但密码错误,超过次数则锁定账号
  131. if (grantType != null && !grantType.equals(TokenUtil.REFRESH_TOKEN_KEY) && !user.getPassword().equals(DigestUtil.hex(password))) {
  132. setFailCount(tenantId, username, count);
  133. throw new UsernameNotFoundException(TokenUtil.USER_NOT_FOUND);
  134. }
  135. // 用户角色不存在
  136. if (Func.isEmpty(userInfo.getRoles())) {
  137. throw new UserDeniedAuthorizationException(TokenUtil.USER_HAS_NO_ROLE);
  138. }
  139. /* 校验登陆账号权限,客户端填报、试验userType=1,app端userType=2,档案userType=3,后管userType=4 ,管控userType=5 ,征拆userType=6 */
  140. if (user.getUserType().contains(("1"))
  141. || user.getUserType().contains(("2"))
  142. || user.getUserType().contains(("3"))
  143. || user.getUserType().contains(("4"))
  144. || user.getUserType().contains(("5"))
  145. || user.getUserType().contains(("6"))
  146. || user.getUserType().contains(("7"))
  147. || user.getUserType().contains(("8"))
  148. ) {
  149. if (!("1,2,3,4,5,6,7,8").equals(user.getUserType())) {
  150. //如果不是全部权限,那么分批校验登陆平台权限
  151. this.judgeLoginPermission(user);
  152. }
  153. } else {
  154. throw new UserDeniedAuthorizationException(TokenUtil.USER_ACCOUNT_NO_TYPE);
  155. }
  156. // 多部门情况下指定单部门
  157. if (Func.isNotEmpty(headerDept) && user.getDeptId().contains(headerDept)) {
  158. user.setDeptId(headerDept);
  159. }
  160. // 多角色情况下指定单角色
  161. if (Func.isNotEmpty(headerRole) && user.getRoleId().contains(headerRole)) {
  162. R<List<String>> roleResult = sysClient.getRoleAliases(headerRole);
  163. if (roleResult.isSuccess()) {
  164. userInfo.setRoles(roleResult.getData());
  165. }
  166. user.setRoleId(headerRole);
  167. }
  168. // 成功则清除登录错误次数
  169. delFailCount(tenantId, username);
  170. return new BladeUserDetails(user.getId(), user.getPhone(),
  171. 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),
  172. username, AuthConstant.ENCRYPT + user.getPassword(), userInfo.getDetail(), true, true, true, true,
  173. AuthorityUtils.commaSeparatedStringToAuthorityList(Func.join(result.getData().getRoles())));
  174. } else {
  175. throw new UsernameNotFoundException(result.getMsg());
  176. }
  177. }
  178. /**
  179. * 获取账号错误次数
  180. *
  181. * @param tenantId 租户id
  182. * @param username 账号
  183. * @return int
  184. */
  185. private int getFailCount(String tenantId, String username) {
  186. return Func.toInt(bladeRedis.get(CacheNames.tenantKey(tenantId, CacheNames.USER_FAIL_KEY, username)), 0);
  187. }
  188. /**
  189. * 设置账号错误次数
  190. *
  191. * @param tenantId 租户id
  192. * @param username 账号
  193. * @param count 次数
  194. */
  195. private void setFailCount(String tenantId, String username, int count) {
  196. bladeRedis.setEx(CacheNames.tenantKey(tenantId, CacheNames.USER_FAIL_KEY, username), count + 1, Duration.ofMinutes(30));
  197. }
  198. /**
  199. * 清空账号错误次数
  200. *
  201. * @param tenantId 租户id
  202. * @param username 账号
  203. */
  204. private void delFailCount(String tenantId, String username) {
  205. bladeRedis.del(CacheNames.tenantKey(tenantId, CacheNames.USER_FAIL_KEY, username));
  206. }
  207. /**
  208. * 校验refreshToken合法性
  209. *
  210. * @param grantType 认证类型
  211. * @param request 请求
  212. */
  213. private boolean judgeRefreshToken(String grantType, HttpServletRequest request) {
  214. if (jwtProperties.getState() && jwtProperties.getSingle() && StringUtil.equals(grantType, TokenUtil.REFRESH_TOKEN_KEY)) {
  215. String refreshToken = request.getParameter(TokenUtil.REFRESH_TOKEN_KEY);
  216. Claims claims = JwtUtil.parseJWT(refreshToken);
  217. String tenantId = String.valueOf(claims.get("tenant_id"));
  218. String userId = String.valueOf(claims.get("user_id"));
  219. String token = JwtUtil.getRefreshToken(tenantId, userId, refreshToken);
  220. return StringUtil.equalsIgnoreCase(token, refreshToken);
  221. }
  222. return true;
  223. }
  224. /**
  225. * 校验登陆账号权限
  226. *
  227. * @param user user信息
  228. * @author liuyc
  229. */
  230. private void judgeLoginPermission(User user) {
  231. //获取平台信息
  232. String clientId = TokenUtil.getClientIdFromHeader();
  233. String result = "0";
  234. if (clientId != null) {
  235. switch (clientId) {
  236. case "client":
  237. result = "1"; //WEB = 客户端
  238. break;
  239. case "uni-app":
  240. result = "2"; //APP = APP端
  241. break;
  242. case "archives":
  243. result = "3"; //archives = 档案
  244. break;
  245. case "saber":
  246. result = "4"; //manger = 后管
  247. break;
  248. case "hac":
  249. result = "5"; //hac = 成本管控系统
  250. break;
  251. case "lar":
  252. result = "6"; //lar = 征拆系统
  253. break;
  254. case "measure":
  255. result = "7"; //measure = 计量系统
  256. break;
  257. case "secure":
  258. result = "8"; //secure = 安全
  259. break;
  260. }
  261. }
  262. if ((("1").equals(result) && user.getUserType().contains("1")) //web
  263. || (("2").equals(result) && user.getUserType().contains("2")) //app
  264. || (("3").equals(result) && user.getUserType().contains("3")) //档案
  265. || (("4").equals(result) && user.getUserType().contains("4")) //后管
  266. || (("5").equals(result) && user.getUserType().contains("5")) // 成本管控系统
  267. || (("6").equals(result) && user.getUserType().contains("6")) // 征拆系统
  268. || (("7").equals(result) && user.getUserType().contains("7")) // 计量系统
  269. || (("8").equals(result) && user.getUserType().contains("8")) // 安全
  270. ) {
  271. //放行
  272. return;
  273. }
  274. throw new UserDeniedAuthorizationException(TokenUtil.USER_ACCOUNT_NO_PERMISSION);
  275. }
  276. }