package com.zy.common.web; import com.alibaba.fastjson.JSON; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.core.annotations.ManagerAuth; import com.core.common.Cools; import com.core.common.R; import com.core.exception.CoolException; import com.zy.common.CodeRes; import com.zy.common.auth.MfaLoginTicketManager; import com.zy.common.auth.PasskeyChallengeManager; import com.zy.common.i18n.I18nMessageService; import com.zy.common.entity.Parameter; import com.zy.common.model.PowerDto; import com.zy.common.model.enums.HtmlNavIconType; import com.zy.common.utils.MfaTotpUtil; import com.zy.common.utils.PasskeyWebAuthnUtil; import com.zy.common.utils.QrCode; import com.zy.common.utils.RandomValidateCodeUtil; import com.zy.system.entity.*; import com.zy.system.service.*; import com.zy.system.timer.LicenseTimer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import jakarta.servlet.http.HttpServletResponse; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; import java.util.Base64; import java.util.*; /** * Created by vincent on 2019-07-30 */ @RestController public class AuthController extends BaseController { @Value("${super.pwd}") private String superPwd; @Autowired private UserService userService; @Autowired private RoleService roleService; @Autowired private UserLoginService userLoginService; @Autowired private ResourceService resourceService; @Autowired private RoleResourceService roleResourceService; @Autowired private PermissionService permissionService; @Autowired private RolePermissionService rolePermissionService; @Autowired private LicenseTimer licenseTimer; @Autowired private I18nMessageService i18nMessageService; @Autowired private MfaLoginTicketManager mfaLoginTicketManager; @Autowired private PasskeyChallengeManager passkeyChallengeManager; @RequestMapping("/login.action") @ManagerAuth(value = ManagerAuth.Auth.NONE, memo = "登录") public R loginAction(String mobile, String password){ //验证许可证是否有效 if (!licenseTimer.getSystemSupport()){ return new R(20001, i18nMessageService.getMessage("response.system.licenseExpired")); } if (Cools.isEmpty(mobile, password)) { return new R(10003, i18nMessageService.getMessage("response.user.passwordMismatch")); } if (mobile.equals("super") && password.equals(Cools.md5(superPwd))) { Map res = new HashMap<>(); res.put("username", mobile); res.put("token", Cools.enToken(System.currentTimeMillis() + mobile, superPwd)); return R.ok(res); } User user = userService.getByMobileWithSecurity(mobile); if (Cools.isEmpty(user)){ return new R(10001, i18nMessageService.getMessage("response.user.notFound")); } if (user.getStatus()!=1){ return new R(10002, i18nMessageService.getMessage("response.user.disabled")); } if (!user.getPassword().equals(password)){ return new R(10003, i18nMessageService.getMessage("response.user.passwordMismatch")); } if (requiresMfa(user)) { Map res = new HashMap<>(); res.put("username", user.getUsername()); res.put("mfaRequired", true); res.put("mfaTicket", mfaLoginTicketManager.create(user.getId())); return R.ok(res); } return R.ok(buildLoginSuccess(user)); } @RequestMapping("/login/mfa.action") @ManagerAuth(value = ManagerAuth.Auth.NONE, memo = "MFA登录") public R loginMfaAction(String ticket, String code) { Long userId = mfaLoginTicketManager.getUserId(ticket); if (userId == null) { return new R(10004, i18nMessageService.getMessage("response.user.mfaTicketExpired")); } User user = userService.getByIdWithSecurity(userId); if (Cools.isEmpty(user)) { mfaLoginTicketManager.remove(ticket); return new R(10001, i18nMessageService.getMessage("response.user.notFound")); } if (user.getStatus() != 1) { mfaLoginTicketManager.remove(ticket); return new R(10002, i18nMessageService.getMessage("response.user.disabled")); } if (!requiresMfa(user)) { mfaLoginTicketManager.remove(ticket); return new R(10005, i18nMessageService.getMessage("response.user.mfaNotEnabled")); } if (!MfaTotpUtil.verifyCode(user.getMfaSecret(), code, 1)) { return new R(10006, i18nMessageService.getMessage("response.user.mfaCodeMismatch")); } mfaLoginTicketManager.remove(ticket); return R.ok(buildLoginSuccess(user)); } @RequestMapping("/login/passkey/options.action") @ManagerAuth(value = ManagerAuth.Auth.NONE, memo = "通行密钥登录参数") public R loginPasskeyOptions(String mobile) { if (!licenseTimer.getSystemSupport()) { return new R(20001, i18nMessageService.getMessage("response.system.licenseExpired")); } String origin = PasskeyWebAuthnUtil.buildOrigin(request); String rpId = PasskeyWebAuthnUtil.buildRpId(request); if (!PasskeyWebAuthnUtil.isSecureOriginAllowed(origin, rpId)) { return new R(10009, i18nMessageService.getMessage("response.user.passkeySecureContextRequired")); } User user = null; if (!Cools.isEmpty(mobile)) { user = userService.getByMobileWithSecurity(mobile); if (Cools.isEmpty(user)) { return new R(10001, i18nMessageService.getMessage("response.user.notFound")); } if (user.getStatus() != 1) { return new R(10002, i18nMessageService.getMessage("response.user.disabled")); } if (!hasPasskeyBound(user)) { return new R(10010, i18nMessageService.getMessage("response.user.passkeyNotBound")); } } PasskeyChallengeManager.ChallengeState state = passkeyChallengeManager.createAuthentication(user == null ? null : user.getId(), origin, rpId); return R.ok(buildPasskeyAuthenticationOptions(state, user)); } @RequestMapping("/login/passkey/verify.action") @ManagerAuth(value = ManagerAuth.Auth.NONE, memo = "通行密钥登录") public R loginPasskeyVerify(String ticket, String credentialId, String clientDataJSON, String authenticatorData, String signature) { if (!licenseTimer.getSystemSupport()) { return new R(20001, i18nMessageService.getMessage("response.system.licenseExpired")); } PasskeyChallengeManager.ChallengeState state = passkeyChallengeManager.get(ticket, PasskeyChallengeManager.Purpose.AUTHENTICATION); if (state == null) { return new R(10011, i18nMessageService.getMessage("response.user.passkeyTicketExpired")); } try { com.alibaba.fastjson.JSONObject clientData = PasskeyWebAuthnUtil.parseClientData(clientDataJSON); PasskeyWebAuthnUtil.validateClientData(clientData, "webauthn.get", state.getChallenge(), state.getOrigin()); PasskeyWebAuthnUtil.AuthenticatorData authData = PasskeyWebAuthnUtil.validateAuthenticatorData(authenticatorData, state.getRpId(), true); User user = state.getUserId() == null ? userService.getByPasskeyCredentialId(credentialId) : userService.getByIdWithSecurity(state.getUserId()); if (Cools.isEmpty(user)) { return new R(10001, i18nMessageService.getMessage("response.user.notFound")); } if (user.getStatus() != 1) { return new R(10002, i18nMessageService.getMessage("response.user.disabled")); } if (!hasPasskeyBound(user) || !Cools.eq(user.getPasskeyCredentialId(), credentialId)) { return new R(10010, i18nMessageService.getMessage("response.user.passkeyNotBound")); } PasskeyWebAuthnUtil.verifyAssertionSignature( user.getPasskeyPublicKey(), user.getPasskeyAlgorithm(), authenticatorData, clientDataJSON, signature ); long nextSignCount = authData.getSignCount(); Long currentSignCount = user.getPasskeySignCount(); if (currentSignCount != null && currentSignCount > 0 && nextSignCount > 0 && nextSignCount <= currentSignCount) { return new R(10012, i18nMessageService.getMessage("response.user.passkeyCounterMismatch")); } userService.update(new com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper() .eq("id", user.getId()) .set("passkey_sign_count", nextSignCount) .set("passkey_last_used_time", new Date())); return R.ok(buildLoginSuccess(user)); } catch (Exception ex) { return new R(10013, i18nMessageService.getMessage("response.user.passkeyVerifyFailed")); } finally { passkeyChallengeManager.remove(ticket); } } @RequestMapping("/code/switch.action") public R code() { return R.ok().add(Parameter.get().getCodeSwitch()); } @RequestMapping("/code.action") public void code(@RequestParam String sd, HttpServletResponse response) { RandomValidateCodeUtil.getRandcode(sd, response); } @RequestMapping("/code.do") public String codeDo(@RequestParam String sd) throws Exception { String code = null; int time = 0; while (time < 3000) { code = RandomValidateCodeUtil.code.get(sd); if (!Cools.isEmpty(code)){ break; } else { Thread.sleep(10); time = time + 100; } } RandomValidateCodeUtil.code.remove(sd); return code; } @RequestMapping("/user/detail/auth") @ManagerAuth public R userDetail(){ User user = userService.getByIdWithSecurity(getUserId()); if (Cools.isEmpty(user)) { return R.ok(); } return R.ok(buildSafeUserDetail(user)); } @RequestMapping("/user/mfa/setup/auth") @ManagerAuth public R userMfaSetup() { User user = userService.getByIdWithSecurity(getUserId()); if (Cools.isEmpty(user)) { return new R(10001, i18nMessageService.getMessage("response.user.notFound")); } if (!Integer.valueOf(1).equals(user.getMfaAllow())) { return new R(10007, i18nMessageService.getMessage("response.user.mfaNotAllowed")); } String secret = MfaTotpUtil.generateSecret(); String account = !Cools.isEmpty(user.getMobile()) ? user.getMobile() : (!Cools.isEmpty(user.getUsername()) ? user.getUsername() : String.valueOf(user.getId())); String otpAuth = MfaTotpUtil.buildOtpAuthUri("WCS", account, secret); Map data = new HashMap<>(); data.put("secret", secret); data.put("otpAuth", otpAuth); data.put("qrCode", renderQrCodeDataUri(otpAuth)); return R.ok(data); } @RequestMapping("/user/mfa/enable/auth") @ManagerAuth @Transactional public R userMfaEnable(String currentPassword, String secret, String code) { User user = userService.getByIdWithSecurity(getUserId()); if (Cools.isEmpty(user)) { return new R(10001, i18nMessageService.getMessage("response.user.notFound")); } if (!Integer.valueOf(1).equals(user.getMfaAllow())) { return new R(10007, i18nMessageService.getMessage("response.user.mfaNotAllowed")); } if (!Cools.eq(user.getPassword(), currentPassword)) { return new R(10008, i18nMessageService.getMessage("response.user.oldPasswordMismatch")); } String normalizedSecret = normalizeSecret(secret); if (Cools.isEmpty(normalizedSecret) || !MfaTotpUtil.verifyCode(normalizedSecret, code, 1)) { return new R(10006, i18nMessageService.getMessage("response.user.mfaCodeMismatch")); } userService.update(new com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper() .eq("id", user.getId()) .set("mfa_allow", 1) .set("mfa_enabled", 1) .set("mfa_secret", normalizedSecret) .set("mfa_bound_time", new Date())); return R.ok(); } @RequestMapping("/user/mfa/disable/auth") @ManagerAuth @Transactional public R userMfaDisable(String currentPassword, String code) { User user = userService.getByIdWithSecurity(getUserId()); if (Cools.isEmpty(user)) { return new R(10001, i18nMessageService.getMessage("response.user.notFound")); } if (!requiresMfa(user)) { return new R(10005, i18nMessageService.getMessage("response.user.mfaNotEnabled")); } if (!Cools.eq(user.getPassword(), currentPassword)) { return new R(10008, i18nMessageService.getMessage("response.user.oldPasswordMismatch")); } if (!MfaTotpUtil.verifyCode(user.getMfaSecret(), code, 1)) { return new R(10006, i18nMessageService.getMessage("response.user.mfaCodeMismatch")); } userService.update(new com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper() .eq("id", user.getId()) .set("mfa_enabled", 0) .set("mfa_secret", null) .set("mfa_bound_time", null)); return R.ok(); } @RequestMapping("/user/passkey/register/options/auth") @ManagerAuth public R userPasskeyRegisterOptions() { User user = userService.getByIdWithSecurity(getUserId()); if (Cools.isEmpty(user)) { return new R(10001, i18nMessageService.getMessage("response.user.notFound")); } if (hasPasskeyBound(user)) { return new R(10014, i18nMessageService.getMessage("response.user.passkeyAlreadyBound")); } String origin = PasskeyWebAuthnUtil.buildOrigin(request); String rpId = PasskeyWebAuthnUtil.buildRpId(request); if (!PasskeyWebAuthnUtil.isSecureOriginAllowed(origin, rpId)) { return new R(10009, i18nMessageService.getMessage("response.user.passkeySecureContextRequired")); } PasskeyChallengeManager.ChallengeState state = passkeyChallengeManager.createRegistration(user.getId(), origin, rpId); return R.ok(buildPasskeyRegistrationOptions(state, user)); } @RequestMapping("/user/passkey/register/finish/auth") @ManagerAuth @Transactional public R userPasskeyRegisterFinish(String ticket, String currentPassword, String name, String credentialId, String clientDataJSON, String authenticatorData, String publicKey, Integer publicKeyAlgorithm, String transports) { User user = userService.getByIdWithSecurity(getUserId()); if (Cools.isEmpty(user)) { return new R(10001, i18nMessageService.getMessage("response.user.notFound")); } if (!Cools.eq(user.getPassword(), currentPassword)) { return new R(10008, i18nMessageService.getMessage("response.user.oldPasswordMismatch")); } if (hasPasskeyBound(user)) { return new R(10014, i18nMessageService.getMessage("response.user.passkeyAlreadyBound")); } PasskeyChallengeManager.ChallengeState state = passkeyChallengeManager.get(ticket, PasskeyChallengeManager.Purpose.REGISTRATION); if (state == null || !Objects.equals(state.getUserId(), user.getId())) { return new R(10011, i18nMessageService.getMessage("response.user.passkeyTicketExpired")); } try { com.alibaba.fastjson.JSONObject clientData = PasskeyWebAuthnUtil.parseClientData(clientDataJSON); PasskeyWebAuthnUtil.validateClientData(clientData, "webauthn.create", state.getChallenge(), state.getOrigin()); PasskeyWebAuthnUtil.AuthenticatorData authData = PasskeyWebAuthnUtil.validateAuthenticatorData(authenticatorData, state.getRpId(), true); PasskeyWebAuthnUtil.ensurePublicKeyMaterial(publicKey, publicKeyAlgorithm); if (Cools.isEmpty(credentialId)) { return new R(10013, i18nMessageService.getMessage("response.user.passkeyRegisterFailed")); } User exist = userService.getByPasskeyCredentialId(credentialId); if (!Cools.isEmpty(exist) && !Objects.equals(exist.getId(), user.getId())) { return new R(10015, i18nMessageService.getMessage("response.user.passkeyCredentialExists")); } userService.update(new com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper() .eq("id", user.getId()) .set("passkey_name", normalizePasskeyName(name)) .set("passkey_credential_id", credentialId) .set("passkey_public_key", publicKey) .set("passkey_algorithm", publicKeyAlgorithm) .set("passkey_sign_count", authData.getSignCount()) .set("passkey_transports", normalizePasskeyTransports(transports)) .set("passkey_bound_time", new Date()) .set("passkey_last_used_time", null)); return R.ok(); } catch (Exception ex) { return new R(10016, i18nMessageService.getMessage("response.user.passkeyRegisterFailed")); } finally { passkeyChallengeManager.remove(ticket); } } @RequestMapping("/user/passkey/remove/auth") @ManagerAuth @Transactional public R userPasskeyRemove(String currentPassword) { User user = userService.getByIdWithSecurity(getUserId()); if (Cools.isEmpty(user)) { return new R(10001, i18nMessageService.getMessage("response.user.notFound")); } if (!hasPasskeyBound(user)) { return new R(10010, i18nMessageService.getMessage("response.user.passkeyNotBound")); } if (!Cools.eq(user.getPassword(), currentPassword)) { return new R(10008, i18nMessageService.getMessage("response.user.oldPasswordMismatch")); } userService.update(new com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper() .eq("id", user.getId()) .set("passkey_name", null) .set("passkey_credential_id", null) .set("passkey_public_key", null) .set("passkey_algorithm", null) .set("passkey_sign_count", 0) .set("passkey_transports", null) .set("passkey_bound_time", null) .set("passkey_last_used_time", null)); return R.ok(); } @RequestMapping("/menu/auth") @ManagerAuth(memo = "首页菜单") public R menu(){ // 获取所有一级菜单 List oneLevel = resourceService.list(new QueryWrapper().eq("level", 1).eq("status", 1).orderBy(true, true, "sort")); User user = null; QueryWrapper resourceWrapper; if (getUserId() == 9527) { resourceWrapper = new QueryWrapper().eq("level", 2).eq("status", 1).orderBy(true, true, "sort"); } else { // 获取当前用户的所有二级菜单 user = userService.getById(getUserId()); List roleResources = roleResourceService.list(new QueryWrapper().eq("role_id", user.getRoleId())); List resourceIds = new ArrayList<>(); roleResources.forEach(roleResource -> resourceIds.add(roleResource.getResourceId())); if (resourceIds.isEmpty()){ return R.ok(); } resourceWrapper = new QueryWrapper().in("id", resourceIds).eq("level", 2).eq("status", 1).orderBy(true, true, "sort"); } List twoLevel = resourceService.list(resourceWrapper); List> result = new ArrayList<>(); for (Resource menu : oneLevel) { Map map = new HashMap<>(); List subMenu = new ArrayList<>(); Iterator iterator = twoLevel.iterator(); while (iterator.hasNext()) { Resource resource = iterator.next(); if (resource.getResourceId() != null && resource.getResourceId().equals(menu.getId())) { // 是否拥有查看权限 if (getUserId() != 9527) { Resource view = firstResource(new QueryWrapper() .eq("resource_id", resource.getId()) .like("code", "view")); if (!Cools.isEmpty(view)){ RoleResource param = new RoleResource(); param.setResourceId(view.getId()); param.setRoleId(user.getRoleId()); if (!existsRoleResource(new QueryWrapper<>(param))){ continue; } } } resource.setName(localizeResourceName(resource)); subMenu.add(resource); iterator.remove(); } } if (subMenu.isEmpty()) { continue; } map.put("menuId", menu.getId()); map.put("menuCode", menu.getCode()); map.put("menuIcon", HtmlNavIconType.get(menu.getCode())); map.put("menu", localizeResourceName(menu)); map.put("subMenu", subMenu); result.add(map); } return R.ok(result); } @RequestMapping("/power/list/auth") @ManagerAuth public R powerList(){ List oneLevels = resourceService.list(new QueryWrapper().eq("level", 1).eq("status", 1).orderBy(true, true, "sort")); List result = new ArrayList<>(); // 一级 for (Resource oneLevel : oneLevels){ List twoLevelsList = new ArrayList<>(); Map oneLevelMap = new HashMap<>(); oneLevelMap.put("title", localizeResourceName(oneLevel)); oneLevelMap.put("id", oneLevel.getId()); oneLevelMap.put("spread", true); oneLevelMap.put("children", twoLevelsList); List twoLevels = resourceService.list(new QueryWrapper().eq("resource_id", oneLevel.getId()).eq("level", 2).eq("status", 1).orderBy(true, true, "sort")); // 二级 for (Resource twoLevel : twoLevels){ Map twoLevelMap = new HashMap<>(); twoLevelMap.put("title", localizeResourceName(twoLevel)); twoLevelMap.put("id", twoLevel.getId()); twoLevelMap.put("spread", false); List threeLevelsList = new ArrayList<>(); twoLevelMap.put("children", threeLevelsList); // 三级 List threeLevels = resourceService.list(new QueryWrapper().eq("resource_id", twoLevel.getId()).eq("level", 3).eq("status", 1).orderBy(true, true, "sort")); for (Resource threeLevel : threeLevels){ Map threeLevelMap = new HashMap<>(); threeLevelMap.put("title", localizeResourceName(threeLevel)); threeLevelMap.put("id", threeLevel.getId()); threeLevelMap.put("checked", false); threeLevelsList.add(threeLevelMap); } twoLevelsList.add(twoLevelMap); } result.add(oneLevelMap); } // 功能模块 Map functions = new HashMap<>(); functions.put("title", i18nMessageService.getMessage("permission.function")); functions.put("id", "function"); functions.put("spread", true); List funcs = new ArrayList<>(); functions.put("children", funcs); List permissions = permissionService.list(new QueryWrapper().eq("status", 1)); for (Permission permission : permissions) { Map func = new HashMap<>(); func.put("title", i18nMessageService.resolvePermissionText(permission.getName(), permission.getAction(), permission.getId())); func.put("id", permission.getAction()); func.put("spread", true); funcs.add(func); } result.add(functions); return R.ok(result); } private Map buildLoginSuccess(User user) { String token = Cools.enToken(System.currentTimeMillis() + user.getMobile(), user.getPassword()); userLoginService.remove(new QueryWrapper().eq("user_id", user.getId()).eq("system_type", "WCS")); UserLogin userLogin = new UserLogin(); userLogin.setUserId(user.getId()); userLogin.setToken(token); userLogin.setSystemType("WCS"); userLoginService.save(userLogin); Map result = new HashMap<>(); result.put("username", user.getUsername()); result.put("token", token); result.put("mfaRequired", false); return result; } private boolean requiresMfa(User user) { return user != null && Integer.valueOf(1).equals(user.getMfaAllow()) && Integer.valueOf(1).equals(user.getMfaEnabled()) && !Cools.isEmpty(user.getMfaSecret()); } private boolean hasPasskeyBound(User user) { return user != null && !Cools.isEmpty(user.getPasskeyCredentialId()) && !Cools.isEmpty(user.getPasskeyPublicKey()); } private Map buildSafeUserDetail(User user) { Map result = new HashMap<>(); result.put("id", user.getId()); result.put("roleName", user.getRoleName()); result.put("username", user.getUsername()); result.put("mobile", user.getMobile()); result.put("createTime$", user.getCreateTime$()); result.put("mfaAllow", user.getMfaAllow()); result.put("mfaAllow$", user.getMfaAllow$()); result.put("mfaEnabled", user.getMfaEnabled()); result.put("mfaEnabled$", user.getMfaEnabled$()); result.put("mfaBoundTime$", user.getMfaBoundTime$()); result.put("mfaMaskedSecret", MfaTotpUtil.maskSecret(user.getMfaSecret())); result.put("passkeyBound", hasPasskeyBound(user)); result.put("passkeyName", user.getPasskeyName()); result.put("passkeyBoundTime$", user.getPasskeyBoundTime$()); result.put("passkeyLastUsedTime$", user.getPasskeyLastUsedTime$()); result.put("passkeyTransports", user.getPasskeyTransports()); return result; } private Map buildPasskeyRegistrationOptions(PasskeyChallengeManager.ChallengeState state, User user) { Map data = new HashMap<>(); data.put("ticket", state.getTicket()); data.put("challenge", state.getChallenge()); data.put("rpId", state.getRpId()); data.put("rpName", "WCS"); data.put("userId", Base64.getUrlEncoder().withoutPadding().encodeToString(PasskeyWebAuthnUtil.buildUserHandle(user.getId()))); data.put("userName", resolvePasskeyUserName(user)); data.put("userDisplayName", resolvePasskeyDisplayName(user)); data.put("timeout", 60000); data.put("attestation", "none"); data.put("pubKeyCredParams", buildPasskeyAlgorithms()); Map authenticatorSelection = new HashMap<>(); authenticatorSelection.put("residentKey", "preferred"); authenticatorSelection.put("userVerification", "required"); data.put("authenticatorSelection", authenticatorSelection); data.put("excludeCredentials", Collections.emptyList()); return data; } private Map buildPasskeyAuthenticationOptions(PasskeyChallengeManager.ChallengeState state, User user) { Map data = new HashMap<>(); data.put("ticket", state.getTicket()); data.put("challenge", state.getChallenge()); data.put("rpId", state.getRpId()); data.put("timeout", 60000); data.put("userVerification", "required"); if (user == null) { data.put("allowCredentials", Collections.emptyList()); } else { data.put("allowCredentials", Collections.singletonList(buildPasskeyDescriptor(user.getPasskeyCredentialId(), user.getPasskeyTransports()))); } return data; } private List> buildPasskeyAlgorithms() { List> result = new ArrayList<>(); result.add(buildPasskeyAlgorithm(-7)); result.add(buildPasskeyAlgorithm(-257)); result.add(buildPasskeyAlgorithm(-8)); return result; } private Map buildPasskeyAlgorithm(int alg) { Map item = new HashMap<>(); item.put("type", "public-key"); item.put("alg", alg); return item; } private Map buildPasskeyDescriptor(String credentialId, String transports) { Map item = new HashMap<>(); item.put("type", "public-key"); item.put("id", credentialId); item.put("transports", parsePasskeyTransports(transports)); return item; } private String renderQrCodeDataUri(String content) { try { BufferedImage image = QrCode.createImg(content, 220); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); ImageIO.write(image, "jpg", outputStream); return "data:image/jpeg;base64," + Base64.getEncoder().encodeToString(outputStream.toByteArray()); } catch (Exception e) { return ""; } } private String normalizeSecret(String secret) { if (Cools.isEmpty(secret)) { return ""; } return String.valueOf(secret) .trim() .replace(" ", "") .replace("-", "") .toUpperCase(Locale.ROOT); } private String resolvePasskeyUserName(User user) { if (!Cools.isEmpty(user.getMobile())) { return user.getMobile(); } if (!Cools.isEmpty(user.getUsername())) { return user.getUsername(); } return String.valueOf(user.getId()); } private String resolvePasskeyDisplayName(User user) { if (!Cools.isEmpty(user.getUsername())) { return user.getUsername(); } return resolvePasskeyUserName(user); } private String normalizePasskeyName(String name) { String value = Cools.isEmpty(name) ? "" : String.valueOf(name).trim(); if (value.length() > 100) { value = value.substring(0, 100); } if (!Cools.isEmpty(value)) { return value; } return "通行密钥-" + new java.text.SimpleDateFormat("yyyyMMddHHmmss").format(new Date()); } private String normalizePasskeyTransports(String transports) { List values = parsePasskeyTransports(transports); return values.isEmpty() ? null : JSON.toJSONString(values); } private List parsePasskeyTransports(String transports) { if (Cools.isEmpty(transports)) { return Collections.emptyList(); } try { List values = JSON.parseArray(transports, String.class); return values == null ? Collections.emptyList() : values; } catch (Exception ignored) { } List result = new ArrayList<>(); for (String value : String.valueOf(transports).split(",")) { String item = value == null ? "" : value.trim(); if (!item.isEmpty()) { result.add(item); } } return result; } private String localizeResourceName(Resource resource) { return i18nMessageService.resolveResourceText(resource.getName(), resource.getCode(), resource.getId()); } @RequestMapping(value = "/power/{roleId}/auth") @ManagerAuth public R get(@PathVariable("roleId") Long roleId) { List result = new ArrayList<>(); // 菜单 List roleResources = roleResourceService.list(new QueryWrapper().eq("role_id", roleId)); for (RoleResource roleResource : roleResources){ Resource resource = resourceService.getById(roleResource.getResourceId()); if (!Cools.isEmpty(resource)){ if (resource.getLevel() == 3){ result.add(resource.getId()); } } } // 功能 List rolePermissions = rolePermissionService.list(new QueryWrapper().eq("role_id", roleId)); for (RolePermission rolePermission : rolePermissions){ Permission permission = permissionService.getById(rolePermission.getPermissionId()); if (!Cools.isEmpty(permission)){ result.add(permission.getAction()); } } return R.ok(result); } @RequestMapping("/power/auth") @ManagerAuth(memo = "授权") @Transactional public R power(Long roleId, String powers){ Role role = roleService.getById(roleId); Long leaderId = role.getLeader(); roleResourceService.remove(new QueryWrapper().eq("role_id", roleId)); rolePermissionService.remove(new QueryWrapper().eq("role_id", roleId)); if (!Cools.isEmpty(powers)){ List dtos = JSON.parseArray(powers, PowerDto.class); for (PowerDto dto : dtos) { Resource resource = resourceService.getOne(new QueryWrapper().eq("id", dto.getTwo()).eq("level", 2)); if (!Cools.isEmpty(resource)) { // 校验上级权限 if (leaderId != null) { RoleResource roleResource = roleResourceService.getOne(new QueryWrapper().eq("role_id", leaderId).eq("resource_id", resource.getId())); if (null == roleResource) { throw new CoolException(resource.getName().concat("无法授权给").concat(role.getName())); } } RoleResource roleResource = new RoleResource(); roleResource.setRoleId(roleId); roleResource.setResourceId(resource.getId()); roleResourceService.save(roleResource); } else { Permission permission = permissionService.getOne(new QueryWrapper().eq("action", dto.getTwo())); if (!Cools.isEmpty(permission)){ RolePermission rolePermission = new RolePermission(); rolePermission.setRoleId(roleId); rolePermission.setPermissionId(permission.getId()); rolePermissionService.save(rolePermission); } } for (String three : dto.getThree()){ Resource resource1 = resourceService.getOne(new QueryWrapper().eq("id", three).eq("level", 3)); if (!Cools.isEmpty(resource1)) { // 校验上级权限 if (leaderId != null) { RoleResource roleResource = roleResourceService.getOne(new QueryWrapper().eq("role_id", leaderId).eq("resource_id", resource1.getId())); if (null == roleResource) { throw new CoolException(resource.getName().concat("的").concat(resource1.getName().concat("无法授权给").concat(role.getName()))); } } RoleResource roleResource = new RoleResource(); roleResource.setRoleId(roleId); roleResource.setResourceId(resource1.getId()); roleResourceService.save(roleResource); } } } } return R.ok(); } @RequestMapping(value = "/power/menu/{resourceId}/auth") @ManagerAuth public R buttonResource(@PathVariable("resourceId") Long resourceId) { List resources; if (getUserId() == 9527) { resources = resourceService.list(new QueryWrapper().eq("level", 3).eq("resource_id", resourceId)); } else { resources = roleResourceService.getMenuButtomResource(resourceId, getUserId()); } for (Resource resource : resources) { resource.setCode(resource.getCode().split("#")[1]); } return R.ok(resources); } private Resource firstResource(QueryWrapper wrapper) { wrapper.last("limit 1"); List list = resourceService.list(wrapper); return list.isEmpty() ? null : list.get(0); } private boolean existsRoleResource(QueryWrapper wrapper) { wrapper.last("limit 1"); return !roleResourceService.list(wrapper).isEmpty(); } }