package com.iailab.module.system.controller.admin.auth; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.date.LocalDateTimeUtil; import cn.hutool.core.util.StrUtil; import com.iailab.framework.common.enums.CommonStatusEnum; import com.iailab.framework.common.enums.UserTypeEnum; import com.iailab.framework.common.pojo.CommonResult; import com.iailab.framework.common.util.object.BeanUtils; import com.iailab.framework.security.config.SecurityProperties; import com.iailab.framework.security.core.LoginUser; import com.iailab.framework.security.core.util.SecurityFrameworkUtils; import com.iailab.module.system.controller.admin.app.vo.AppMenuRespVO; import com.iailab.module.system.controller.admin.app.vo.AppRespVO; import com.iailab.module.system.controller.admin.auth.vo.*; import com.iailab.module.system.controller.admin.permission.vo.menu.MenuListReqVO; import com.iailab.module.system.controller.admin.permission.vo.menu.MenuRespVO; import com.iailab.module.system.convert.auth.AuthConvert; import com.iailab.module.system.dal.dataobject.app.AppDO; import com.iailab.module.system.dal.dataobject.permission.MenuDO; import com.iailab.module.system.dal.dataobject.permission.RoleDO; import com.iailab.module.system.dal.dataobject.user.AdminUserDO; import com.iailab.module.system.enums.logger.LoginLogTypeEnum; import com.iailab.module.system.enums.permission.MenuTypeEnum; import com.iailab.module.system.service.app.AppService; import com.iailab.module.system.service.auth.AdminAuthService; import com.iailab.module.system.service.permission.MenuService; import com.iailab.module.system.service.permission.PermissionService; import com.iailab.module.system.service.permission.RoleService; import com.iailab.module.system.service.social.SocialClientService; import com.iailab.module.system.service.user.AdminUserService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameters; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.extern.slf4j.Slf4j; import org.springframework.security.core.Authentication; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import javax.annotation.security.PermitAll; import javax.servlet.http.HttpServletRequest; import javax.validation.Valid; import java.util.*; import java.util.stream.Collectors; import static com.iailab.framework.common.pojo.CommonResult.success; import static com.iailab.framework.common.util.collection.CollectionUtils.convertSet; import static com.iailab.framework.security.core.util.SecurityFrameworkUtils.*; import static com.iailab.framework.tenant.core.context.TenantContextHolder.getTenantId; @Tag(name = "管理后台 - 认证") @RestController @RequestMapping("/system/auth") @Validated @Slf4j public class AuthController { @Resource private AdminAuthService authService; @Resource private AdminUserService userService; @Resource private RoleService roleService; @Resource private MenuService menuService; @Resource private PermissionService permissionService; @Resource private SocialClientService socialClientService; @Resource private SecurityProperties securityProperties; @Resource private AppService appService; @PostMapping("/login") @PermitAll @Operation(summary = "使用账号密码登录") public CommonResult login(@RequestBody @Valid AuthLoginReqVO reqVO) { return success(authService.login(reqVO)); } @PostMapping("/logout") @PermitAll @Operation(summary = "登出系统") public CommonResult logout(HttpServletRequest request) { String token = SecurityFrameworkUtils.obtainAuthorization(request, securityProperties.getTokenHeader(), securityProperties.getTokenParameter()); if (StrUtil.isNotBlank(token)) { authService.logout(token, LoginLogTypeEnum.LOGOUT_SELF.getType()); } return success(true); } @PostMapping("/refresh-token") @PermitAll @Operation(summary = "刷新令牌") @Parameter(name = "refreshToken", description = "刷新令牌", required = true) public CommonResult refreshToken(@RequestParam("refreshToken") String refreshToken) { return success(authService.refreshToken(refreshToken)); } @PostMapping("/client-refresh-token") @PermitAll @Operation(summary = "刷新令牌") @Parameter(name = "refreshToken", description = "刷新令牌", required = true) public Map refreshToken(@RequestParam("refreshToken") String refreshToken, @RequestParam("clientId") String clientId) { AuthLoginRespVO authLoginRespVO = authService.refreshToken(refreshToken, clientId); Map map = new HashMap<>(); map.put("access_token", authLoginRespVO.getAccessToken()); map.put("refresh_token", authLoginRespVO.getRefreshToken()); map.put("expires_time", LocalDateTimeUtil.toEpochMilli(authLoginRespVO.getExpiresTime()) / 1000L); return map; } @GetMapping("/get-permission-info") @Operation(summary = "获取登录用户的权限信息") public CommonResult getPermissionInfo() { // 1.1 获得用户信息 AdminUserDO user = userService.getUser(getLoginUserId()); if (user == null) { return success(null); } // 1.2 获得角色列表 Set roleIds = permissionService.getUserRoleIdListByUserId(getLoginUserId()); if (CollUtil.isEmpty(roleIds)) { return success(AuthConvert.INSTANCE.convert(user, Collections.emptyList(), Collections.emptyList())); } List roles = roleService.getRoleList(roleIds); roles.removeIf(role -> !CommonStatusEnum.ENABLE.getStatus().equals(role.getStatus())); // 移除禁用的角色 // 1.3 获得菜单列表 Set menuIds = permissionService.getRoleMenuListByRoleId(convertSet(roles, RoleDO::getId)); List menuList = menuService.getMenuList(menuIds); menuList = menuService.filterDisableMenus(menuList); menuList = menuService.filterMenus(menuList, "system"); // 2. 拼接结果返回 return success(AuthConvert.INSTANCE.convert(user, roles, menuList)); } @GetMapping("/get-app-permission-info") @Operation(summary = "脚手架获取登录用户的权限信息") public CommonResult getAppPermissionInfo() { // 1.1 获得用户信息 AdminUserDO user = userService.getUser(getLoginUserId()); if (user == null) { return success(null); } // 1.2 获得角色列表 Set roleIds = permissionService.getUserRoleIdListByUserId(getLoginUserId()); if (CollUtil.isEmpty(roleIds)) { return success(AuthConvert.INSTANCE.convert(user, Collections.emptyList(), Collections.emptyList())); } List roles = roleService.getRoleList(roleIds); roles.removeIf(role -> !CommonStatusEnum.ENABLE.getStatus().equals(role.getStatus())); // 移除禁用的角色 // 1.3 获得菜单列表 Set menuIds = permissionService.getRoleMenuListByRoleId(convertSet(roles, RoleDO::getId)); List menuList = menuService.getMenuList(menuIds); menuList = menuService.filterDisableMenus(menuList); menuList = menuService.filterMenus(menuList, "app"); // 2. 拼接结果返回 return success(AuthConvert.INSTANCE.convert(user, roles, menuList)); } @GetMapping("/get-app-permission") @Operation(summary = "获取登录用户的app权限信息") public CommonResult> getAppPermission() { List appList = new ArrayList<>(); // 1.1 获得用户信息 AdminUserDO user = userService.getUser(getLoginUserId()); if (user == null) { return success(null); } // 1.2 获得角色列表 Set roleIds = permissionService.getUserRoleIdListByUserId(getLoginUserId()); if (CollUtil.isEmpty(roleIds)) { return success(appList); } List roles = roleService.getRoleList(roleIds); roles.removeIf(role -> !CommonStatusEnum.ENABLE.getStatus().equals(role.getStatus())); // 移除禁用的角色 // 1.3 获得应用菜单列表 Set menuIds = permissionService.getRoleMenuListByRoleId(convertSet(roles, RoleDO::getId)); List menuList = menuService.getMenuList(menuIds); //只要一级菜单,一级菜单即是应用 menuList = menuList.stream().filter(menu -> menu.getParentId() == 0l).collect(Collectors.toList()); menuList = menuService.filterDisableMenus(menuList); List ids = menuList.stream().map(MenuDO::getAppId).collect(Collectors.toList()); List appDOS = appService.selectBatchIds(ids); //排序 Collections.sort(appDOS, Comparator.comparing(AppDO::getOrderNum)); // 2. 拼接结果返回 return success(BeanUtils.toBean(appDOS, AppRespVO.class)); } @GetMapping("/get-app-menu-permission") @Operation(summary = "获取登录用户的app权限信息") public CommonResult> getAppMenuPermission(@RequestParam("id") Long id) { List menuVOS = new ArrayList<>(); // 1.1 获得用户信息 AdminUserDO user = userService.getUser(getLoginUserId()); if (user == null) { return success(null); } // 1.2 获得角色列表 Set roleIds = permissionService.getUserRoleIdListByUserId(getLoginUserId()); if (CollUtil.isEmpty(roleIds)) { return success(menuVOS); } List roles = roleService.getRoleList(roleIds); roles.removeIf(role -> !CommonStatusEnum.ENABLE.getStatus().equals(role.getStatus())); // 移除禁用的角色 // 1.3 获得应用菜单列表 Set menuIds = permissionService.getRoleMenuListByRoleId(convertSet(roles, RoleDO::getId)); List menuList = menuService.getMenuList(menuIds); menuList = menuService.filterDisableMenus(menuList); MenuDO menuDO = menuService.getMenuByAppId(id); AppDO info = appService.getInfo(id); List children = new LinkedList<>(); // 遍历每一层 Collection parentIds = Collections.singleton(menuDO.getId()); for (int i = 0; i < Short.MAX_VALUE; i++) { // 使用 Short.MAX_VALUE 避免 bug 场景下,存在死循环 // 查询当前层,所有的子应用菜单 List menus = menuService.selectListByParentId(parentIds); // 1. 如果没有子菜单,则结束遍历 if (CollUtil.isEmpty(menus)) { break; } // 2. 如果有子应用菜单,继续遍历 children.addAll(menus); parentIds = convertSet(menus, MenuDO::getId); } children.retainAll(menuList); List tempChildren = new LinkedList<>(); //为每一个二级菜单(非外链菜单)增加一个隐藏父级目录 children.stream().forEach(menu -> { if (menu.getParentId().equals(menuDO.getId())) { if(menu.getType().equals(MenuTypeEnum.MENU.getType())) { MenuDO parentMenu = BeanUtils.toBean(menu, MenuDO.class); parentMenu.setId(System.currentTimeMillis() + (int) (Math.random() * (99999 - 10000 + 1)) + 10000); parentMenu.setType(MenuTypeEnum.DIR.getType()); parentMenu.setVisible(true); parentMenu.setAlwaysShow(false); parentMenu.setParentId(menuDO.getId()); parentMenu.setPath("/"); menu.setParentId(parentMenu.getId()); tempChildren.add(parentMenu); } else if(menu.getType().equals(MenuTypeEnum.DIR.getType())) { // 为应用菜单二级目录前增加“/” if(!menu.getPath().contains("http:") && !menu.getPath().contains("https:")) { menu.setPath("/" + menu.getPath()); } } } tempChildren.add(menu); }); menuVOS = AuthConvert.INSTANCE.buildMenuTree(tempChildren, menuDO.getId(), menuDO.getPath(), info.getType()); // 2. 拼接结果返回 return success(menuVOS); } // ========== 短信登录相关 ========== @PostMapping("/sms-login") @PermitAll @Operation(summary = "使用短信验证码登录") public CommonResult smsLogin(@RequestBody @Valid AuthSmsLoginReqVO reqVO) { return success(authService.smsLogin(reqVO)); } @PostMapping("/send-sms-code") @PermitAll @Operation(summary = "发送手机验证码") public CommonResult sendLoginSmsCode(@RequestBody @Valid AuthSmsSendReqVO reqVO) { authService.sendSmsCode(reqVO); return success(true); } // ========== 社交登录相关 ========== @GetMapping("/social-auth-redirect") @PermitAll @Operation(summary = "社交授权的跳转") @Parameters({ @Parameter(name = "type", description = "社交类型", required = true), @Parameter(name = "redirectUri", description = "回调路径") }) public CommonResult socialLogin(@RequestParam("type") Integer type, @RequestParam("redirectUri") String redirectUri) { return success(socialClientService.getAuthorizeUrl( type, UserTypeEnum.ADMIN.getValue(), redirectUri)); } @PostMapping("/social-login") @PermitAll @Operation(summary = "社交快捷登录,使用 code 授权码", description = "适合未登录的用户,但是社交账号已绑定用户") public CommonResult socialQuickLogin(@RequestBody @Valid AuthSocialLoginReqVO reqVO) { return success(authService.socialLogin(reqVO)); } }