authing
Starting February 26, 2025, Authing Public Cloud free user pools will be entitled to 50 mailings per day, paid user pools will be unaffected, and user pools using custom mail service providers will be unaffected. If you have any questions, please contact Customer Service at 400-888-2106.
authing
Let's talk
音乐平台项目实例
本项目将以一个 Github 上开源的音乐管理系统为例,介绍如何接入 Authing 服务到一个现成系统,帮助你快速开启数字化征程~

项目介绍

引用的开源项目地址:

Gitee:https://gitee.com/Yin-hongwei/music-website

Github:https://github.com/Yin-Hongwei/music-website

项目前后端分离,前端采用 Vue3 + Typescript,后端采用 SpringBoot,均是具有代表性的技术栈。

项目说明文档中已经详细的阐述了如何部署到本地,此处仅补充说明:

1. 通过百度云盘下载项目的音乐、图片资源(非必要)

2. 修改配置文件(Mysql,Redis)

3. 分别启动前后端服务(前端分为管理端和用户端,共三个服务)

4. 两个前端项目依赖安装建议跟随说明文档采用 yarn 命令安装依赖

项目仅作示例展示用,接下来将具体介绍如何接入 Authing 服务,有多种方式,本文侧重点在于介绍通过不改变原本项目前端 UI,而在后端「调用 SDK 」的方式。

预备知识

如果你还没有用过 Authing,可以点击这里查看如何创建一个自己的应用。

对 Authing 的任何名词/概念有疑问:https://docs.authing.co/v2/concepts/

接下来,假设你已经对 Authing 有初步了解,且在 Authing 控制台创建了一个自己的应用。

全局配置

首先要在 pom.xml 文件中引入 Authing 提供的 SDK 依赖:
<dependency> <groupId>cn.authing</groupId> <artifactId>authing-java-sdk</artifactId> <version>{LATEST_VERSION}</version> </dependency>
最新的版本号 LATEST_VERSION 需要你根据 groupId 和 artifactId 自行到 Maven 官网查找。

引入 Authing 提供的 SDK 之后,要获取Authing 服务最核心的认证侧类 AuthenticationClient管理侧类 ManagementClient,通过调用这两个类中封装好的方法,就可以使用 Authing 提供的服务。

可以通过在后端项目中添加 config 配置类,将AuthenticationClientManagementClient 作为 Bean 注入到 Spring之中,此后在各处仅需获取 Bean 并使用即可(例如:@Autowired)。

具体地,在项目主程序同级目录下创建 config 文件夹,在该文件夹中新建 AuthingConfig 类。

示例代码如下:
@Configuration public class AuthingConfig { @Bean @Scope("prototype") public AuthenticationClient authenticationClient() throws IOException, ParseException { //在构造函数中分别填入自己的 App ID、App Secret、APP Host。 AuthenticationClientOptions options = new AuthenticationClientOptions(); options.setAppId("YOUR_APP_ID"); options.setAppSecret("YOUR_APP_SECRET"); options.setAppHost("YOUR_APP_HOST"); AuthenticationClient client = new AuthenticationClient(options); return client; } @Bean @Scope("prototype") public ManagementClient managementClient() { //在构造函数中分别填入自己的 用户池 ID、用户池 Secret、APP Host ManagementClientOptions options = new ManagementClientOptions(); options.setAccessKeyId("YOUR_APP_ID"); options.setAccessKeySecret("YOUR_APP_SECRET"); options.setHost("YOUR_APP_HOST"); ManagementClient client = new ManagementClient(options); return client; } }

如果你不知道如何获取这些数据:

App ID、App Secret、APP Host:https://docs.dev.authing-inc.co/v2/guides/faqs/get-app-id-and-secret.html

用户池 ID、用户池 Secret:https://docs.authing.co/v2/guides/faqs/get-userpool-id-and-secret.html

登录/注册

1. 登录
刚初始化的系统建议自己在 Authing 控制台手动创建一个「超级管理员」账号用于登录
管理端前端登录界面展示:
authing
管理端后端 AdminController 中获取用户名和密码参数,并调用 Authing 提供的 SDK
LoginTokenRespDto loginTokenRespDto = authenticationClient.signInByUsernamePassword( username, password, new SignInOptionsDto() );
SignInOptionsDto 参数非必需,其含义可以在后文补充说明的连接中自行下载并阅读源码。 LoginTokenRespDto 为 Authing 提供的 SDK 返回的统一格式的对象,包含的属性如下图所示:
authing

重点关注:

statusCode,代表请求成功/失败
data,请求返回的数据
有了这些数据可以进一步进行登录验证的判断:
if (loginTokenRespDto.getStatusCode() == 200) { return new SuccessMessage("登录成功", loginTokenRespDto.getData().getAccessToken()).getMessage(); } else { // 该项目未进行错误码区分,故此处仅传递错误信息; // Authing 定义的错误码可以通过 loginTokenRespDto.getApiCode() 获取 return new ErrorMessage(loginTokenRespDto.getMessage()).getMessage(); }

此处登录成功后返回的 data 中会包含 accessToken,该字符串是 Authing 的接口访问凭据,建议将其返回到前端,存储在 cookie 中,之后后端接受的部分请求将从 cookie 中获取 accessToken 并对其进行验证。

注意:前端保存和后端读取要注意区分管理端和用户端的 cookie 名称,即区分不同端登录的用户。

此处给出一种前端保存 cookie 的办法的代码:

命令行
npm install vue-cookies --save
main.ts
import VueCookies from 'vue-cookies' const app = createApp(App); app.config.globalProperties.$cookies = VueCookies;
Login.vue
import { getCurrentInstance } from "vue"; let internalInstance = getCurrentInstance(); let cookies = internalInstance.appContext.config.globalProperties.$cookies; async function submitForm() { let params = new URLSearchParams(); params.append("username", ruleForm.username); params.append("password", ruleForm.password); const result = (await HttpManager.getLoginStatus(params)) as ResponseBody; (proxy as any).$message({ message: result.message, type: result.type, }); if (result.success){ cookies.set("accessToken",result.data); routerManager(RouterName.Info, { path: RouterName.Info }); } }
用户端登录逻辑和管理端类似,只是 Controller不同,此处仅展示前端界面截图:
authing
2. 登出
管理端前端界面:
authing
后端 AdminController 代码:
@PostMapping("admin/logout") public Object logout(@CookieValue(value = "manageAccessToken",required = false) String accessToken){ if(StrUtil.isBlank(accessToken)){ return new ErrorMessage("accessToken 已失效,请重新登录").getMessage(); } Boolean flag = authenticationClient.revokeToken(accessToken); if(flag){ return new SuccessMessage<>("退出登录").getMessage(); }else{ return new ErrorMessage("撤销accessToken出错").getMessage(); } }
用户端登出逻辑和管理端类似,此处仅展示前端界面截图:
authing
3. 注册

需要注意的是,该项目前端中管理端不提供注册功能,只有用户端可以注册。

用户端前端注册界面:
authing
后端 Controller 代码:
// 创建注册配置项 signUpDto SignUpDto signUpDto = new SignUpDto(); // 设置连接方式 signUpDto.setConnection(SignUpDto.Connection.PASSWORD); // 设置账号密码到 passwordDto SignUpByPasswordDto passwordDto = new SignUpByPasswordDto(); passwordDto.setUsername(username); passwordDto.setPassword(password); // 设置 passwordDto 到 signUpDto signUpDto.setPasswordPayload(passwordDto); // 设置其他信息到 profileDto SignUpProfileDto profileDto = new SignUpProfileDto(); // 性别的额外逻辑处理 if ("0".equals(sex)) { profileDto.setGender(SignUpProfileDto.Gender.F); } else if ("1".equals(sex)) { profileDto.setGender(SignUpProfileDto.Gender.M); } else { profileDto.setGender(SignUpProfileDto.Gender.U); } profileDto.setBirthdate(birth); profileDto.setAddress(location); profileDto.setPhoto(avator); profileDto.setProfile(introduction); // 设置 profileDto 到 signUpDto signUpDto.setProfile(profileDto); // 调用 authing 接口 (注册用户) UserSingleRespDto userSingleRespDto = authenticationClient.signUp(signUpDto);

可以看出 Authing 提供的 SDK 包含了该项目用户所需的全部属性,可以完全替代原本的后端接口,当然,有些地方需要你根据实际项目作额外的逻辑处理。

需要补充的一点是,原示例系统中,用户注册时需要填手机号和邮箱,用户实体类中也包含这两条属性,此处删除。因为 Authing 服务将手机号/邮箱和用户进行绑定时,会向手机号/邮箱发送验证邮件,用户需在规定时间内填写验证码才能通过验证并成功绑定。

如业务需要,此处提供一些 JAVA SDK:

绑定邮箱:AuthenticationClient.bindEmail()
绑定手机号:AuthenticationClient.bindPhone()
修改用户信息:AuthenticationClient.updateProfile()

更多 JAVA SDK 可以自行查看 AuthenticationClient/ManagementClient 源码,更底层的开放 API 可以看Authing 开放 API文档。

4. Authing Guard 登录/注册
相比原用户端界面增加了如下两个入口:
authing
内嵌使用,即将 Authing 的登录窗挂载到你的 dom 节点,效果如下:
authing
托管使用,即跳转到 Authing 管理的前端页面进行登录,登录完成后回调回自己的服务,效果如下:
authing

使用 Guard 进行登录需要注意以下几点:

关于 accessToken 的获取方式改变:Guard 登录成功后返回的用户信息中包含了属性名为 token 的 ID Token,然后参考文档使用 ID Token 换取 Access Token,自己换取 AccessToken。示例代码中已经做好,但你仍需要参考该文档进行控制台的额外配置。
为了兼容原本的账号密码登录,需要进行额外逻辑处理:作普通登录和 Guard 登录、登出的判断。

用户管理

上面介绍登录/注册时,细心的你可能注意到了,只有用户端可以进行注册,管理端是无法进行注册的。那么怎么解决管理端的账号问题呢?

首先,系统会默认创建一个超级管理员角色,此处以 superAdmin 为例,给出在 Authing 的控制台创建角色的示例,当然你也可以自己用其他方式(比如调用 SDK)解决这个问题。

当然,Authing 控制台此处也可以创建更多用户

首先打开你的应用,找到用户管理 - 用户列表 - 创建用户,点击(示图中已经创建完成)
authing
弹框如下:
authing

建议根据你所使用的系统中的实际情况来决定创建方式,示例系统中仅有用户名+密码的方式,所以此处使用用户名方式创建 superAdmin 用户。

这样你就可以登录管理端了。你可能会有另一个疑问,那么用户端创建的用户难道也可以登录管理端吗?现在用户端和管理端的用户不是放在一起了吗?没错,用户端和管理端的用户确实放在了一起(建议如此,也可以不放在一起)。但是我们可以给用户赋予不同角色,来区分用户端和管理端的用户,这点会在下文角色管理详细说明。

管理端可以完成的操作如下:
登录后,界面如下:
authing

示例系统中,可以对用户进行列表展示和删除/批量删除的操作,下面展示使用 Authing 提供的 SDK 代替原本的代码:

列表展示
后端 ConsumerController 代码:
@RequestMapping(value = "/user", method = RequestMethod.GET) public Object allUser() { int page = 1, limit = 10; List<UserDto> userList = new ArrayList<>(); ListUsersRequestDto listUsersRequestDto = new ListUsersRequestDto(); ListUsersOptionsDto optionsDto = new ListUsersOptionsDto(); PaginationDto paginationDto = new PaginationDto(); paginationDto.setPage(page); paginationDto.setLimit(limit); optionsDto.setPagination(paginationDto); listUsersRequestDto.setOptions(optionsDto); // 调用 authing 接口 (获取所有用户列表) UserPaginatedRespDto users = managementClient.listUsers(listUsersRequestDto); userList.addAll(users.getData().getList()); // 循环分页处理, 返回全量数据 while (users.getData().getTotalCount() - userList.size() > 0) { paginationDto.setPage(++page); paginationDto.setLimit(limit); optionsDto.setPagination(paginationDto); listUsersRequestDto.setOptions(optionsDto); // 调用 authing 接口 (再次分页获取数据) users = managementClient.listUsers(listUsersRequestDto); userList.addAll(users.getData().getList()); } List<Consumer> consumers = convertConsumers(userList); return new SuccessMessage<List<Consumer>>(null, consumers).getMessage(); }

需要注意的是, Authing 提供的 SDK 中默认有分页的功能,将数据按照 page=1,limit=10 来返回,示例代码中,由于前端原本代码中自带分页功能,故将其合并到了一个 List 之中。

还有一点需要注意,要将 Authing 提供的 SDK 中返回的 UserDto 对象映射成你实际的用户对象。

删除
后端 ConsumerController 代码:
@GetMapping("/user/delete") public Object deleteUserByAdmin(HttpServletRequest req){ String id = req.getParameter("id"); DeleteUsersBatchDto deleteDto = new DeleteUsersBatchDto(); deleteDto.setUserIds(Collections.singletonList(id)); // 调用 authing 接口 (根据 userId 删除用户) boolean res = managementClient.deleteUsersBatch(deleteDto).getData().getSuccess(); if (res) { return new SuccessMessage<Null>("删除成功").getMessage(); } else { return new ErrorMessage("删除失败").getMessage(); } }
批量删除

原系统在前端采用循环调用删除来实现。

用户端可以完成的操作如下:
修改个人资料
前端界面:
authing
后端 ConsumerController 代码:
@ResponseBody @RequestMapping(value = "/user/update", method = RequestMethod.POST) public Object updateUserMsg(HttpServletRequest req) { String id = req.getParameter("id").trim(); String username = req.getParameter("username").trim(); String sex = req.getParameter("sex").trim(); String birth = req.getParameter("birth").trim(); String introduction = req.getParameter("introduction").trim(); String location = req.getParameter("location").trim(); // 更新字段填充 UpdateUserReqDto updateDto = new UpdateUserReqDto(); updateDto.setUserId(id); updateDto.setUsername(username); updateDto.setBirthdate(birth); updateDto.setProfile(introduction); updateDto.setAddress(location); if ("0".equals(sex)) { updateDto.setGender(UpdateUserReqDto.Gender.F); } else if ("1".equals(sex)) { updateDto.setGender(UpdateUserReqDto.Gender.M); } else { updateDto.setGender(UpdateUserReqDto.Gender.U); } // 调用 authing 接口 UserSingleRespDto updateUser = managementClient.updateUser(updateDto); if (updateUser.getStatusCode() == 200) { return new SuccessMessage<Null>("修改成功").getMessage(); } else { return new ErrorMessage("修改失败").getMessage(); } }
更新密码:
前端界面:
authing
后端 ConsumerController 代码:
@ResponseBody @RequestMapping(value = "/user/updatePassword", method = RequestMethod.POST) public Object updatePassword(HttpServletRequest req,@CookieValue("userAccessToken") String accessToken) { // 配置 accessToken authenticationClient.setAccessToken(accessToken); // String id = req.getParameter("id").trim(); // String username = req.getParameter("username").trim(); String old_password = req.getParameter("old_password").trim(); String password = req.getParameter("password").trim(); UpdatePasswordDto updatePasswordDto = new UpdatePasswordDto(); updatePasswordDto.setOldPassword(old_password); updatePasswordDto.setNewPassword(password); updatePasswordDto.setPasswordEncryptType(UpdatePasswordDto.PasswordEncryptType.NONE); CommonResponseDto commonResponseDto = authenticationClient.updatePassword(updatePasswordDto); if (commonResponseDto.getStatusCode() == 200) { return new SuccessMessage<Null>("密码修改成功").getMessage(); } else { return new ErrorMessage(commonResponseDto.getMessage()).getMessage(); } }

此处 updatePassword() 调用的前提是在 authenticationClient 中配置 accessToken(之后也会有类似情况),因为 accessToken 携带了用户信息,所以不再需要传递id、username 等信息,仅需要旧密码和新密码,就可以修改密码。

自我注销
前端界面:
authing
后端 ConsumerController 代码:
@RequestMapping(value = "/user/deleteSelf", method = RequestMethod.POST) public Object deleteUserBySelf(HttpServletRequest req,@CookieValue("userAccessToken") String accessToken) { // String id = req.getParameter("id"); String password = req.getParameter("password"); authenticationClient.setAccessToken(accessToken); VerifyDeleteAccountRequestDto verifyDeleteAccountRequestDto = new VerifyDeleteAccountRequestDto(); verifyDeleteAccountRequestDto.setVerifyMethod(VerifyDeleteAccountRequestDto.VerifyMethod.PASSWORD); DeleteAccountByPasswordDto deleteAccountByPasswordDto = new DeleteAccountByPasswordDto(); deleteAccountByPasswordDto.setPassword(password); verifyDeleteAccountRequestDto.setPasswordPayload(deleteAccountByPasswordDto); // 调用 authing 接口 VerifyDeleteAccountRequestRespDto verifyDeleteAccountRequestRespDto = authenticationClient.verifyDeleteAccountRequest(verifyDeleteAccountRequestDto); if(verifyDeleteAccountRequestRespDto.getStatusCode() != 200){ return new ErrorMessage(verifyDeleteAccountRequestRespDto.getMessage()).getMessage(); } String deleteAccountToken = verifyDeleteAccountRequestRespDto.getData().getDeleteAccountToken(); DeleteAccounDto deleteAccounDto = new DeleteAccounDto(); deleteAccounDto.setDeleteAccountToken(deleteAccountToken); // 调用 authing 接口 authenticationClient.deleteAccount(deleteAccounDto); return new SuccessMessage<>("删除成功").getMessage(); }
注意区分用户自我注销和管理员删除的区别,本质在于权限,体现在调用的是 authenticationClient 还是 managementClient

角色管理

书接上文,管理端和用户端账号的区分,依赖于角色,下面将介绍示例代码中对于整个系统的角色划分,当然你可以自定义。最初,我们给系统创建了一个 superAdmin 用户,现在,我们赋予它 superAdmin 角色。

用 superAdmin 账号登录管理端,找到角色管理 - 新增角色,点击(示图中已经完成创建)。
authing
弹窗如下:
authing
新增角色 RoleController 代码:
@PostMapping("role/add") public Object addRole(HttpServletRequest req){ String code = req.getParameter("code").trim(); String description = req.getParameter("description").trim(); CreateRoleDto createRoleDto = new CreateRoleDto(); createRoleDto.setCode(code); createRoleDto.setDescription(description); createRoleDto.setNamespace(authenticationClient.getOptions().getAppId()); // 调用 authing 接口 RoleSingleRespDto roleSingleRespDto = managementClient.createRole(createRoleDto); if(roleSingleRespDto.getStatusCode() == 200){ return new SuccessMessage<>("添加成功").getMessage(); }else { return new ErrorMessage(roleSingleRespDto.getMessage()).getMessage(); } }
删除角色 RoleController 代码:
@GetMapping("role/delete") public Object deleteRole(HttpServletRequest req){ String code = req.getParameter("code"); DeleteRoleDto deleteRoleDto = new DeleteRoleDto(); // 支持批量删除 List<String> codeList = new ArrayList<>(); codeList.add(code); deleteRoleDto.setCodeList(codeList); deleteRoleDto.setNamespace(authenticationClient.getOptions().getAppId()); // 调用 authing 接口 IsSuccessRespDto isSuccessRespDto = managementClient.deleteRolesBatch(deleteRoleDto); if(isSuccessRespDto.getStatusCode() == 200){ return new SuccessMessage<>("删除成功").getMessage(); }else { return new ErrorMessage(isSuccessRespDto.getMessage()).getMessage(); } }

批量删除原系统在前端采用循环调用删除来实现。

之后,依次创建你所需要的角色,此示例系统中,给定四个角色如下:
authing

其中,superAdmin 是系统的第一个默认超级管理员账号;在用户端创建的账号,创建成功后默认赋予 user 角色,仅可以登录用户端;vip 角色,也是仅可以登录用户端,但由于示例系统是音乐系统,此处给出区别与普通角色的 vip 角色;admin 角色,可以登录用户端和管理端。

一个账号可以拥有多个角色;默认对外不显示 superAdmin 角色的存在;账号不允许撤回 user 角色。

后端 AdminController 代码添加:
//判断用户是不是管理员 boolean isAdmin = false; for(String role:roleList){ if(RoleCodeEnum.ADMIN.getValue().equals(role) || RoleCodeEnum.SUPER_ADMIN.getValue().equals(role)){ isAdmin = true; break; } } if (loginTokenRespDto.getStatusCode() == 200) { if(isAdmin){ // 只有请求成功且是管理员才能登录后台系统,同时将 accessToken 返回到前端 return new SuccessMessage<String>("登录成功",loginTokenRespDto.getData().getAccessToken()).getMessage(); }else{ return new ErrorMessage("只有管理员才能登录后台管理系统").getMessage(); } } else { // 该项目未进行错误码区分,故此处仅传递错误信息;Authing 定义的错误码可以通过 loginTokenRespDto.getApiCode() 获取 return new ErrorMessage(loginTokenRespDto.getMessage()).getMessage(); }
此处要修改之前登录的代码逻辑,需要判断用户角色是否是 admin 或者 superAdmin。
拥有 superAdmin 或者 admin 角色的账号登录管理端之后,可以修改用户的角色:
authing
弹窗如下:
authing
前端代码: ConsumerPage.vue
<!-- template部分 --> <el-dialog title="修改角色" v-model="roleVisible" @close="roleVisible = false" width="300px" center> <el-form label-width="60px" :model="roleEditForm" :rules="roleRule"> <el-form-item prop="roleCodes"> <el-select v-model="roleEditForm.roleCodes" multiple placeholder="请选择角色"> <el-option v-for="item in roleEditForm.roleOptions" :key="item.code" :label="item.code" :value="item.code" ></el-option> </el-select> </el-form-item> </el-form> <template #footer> <span class="dialog-footer"> <el-button @click="roleVisible = false">取 消</el-button> <el-button type="primary" @click="saveRoleEdit">确 定</el-button> </span> </template> </el-dialog> /** * typescript部分 */ const roleVisible = ref(false); const editRoleCode = ref("-1"); const containsUser = (rule,value,callback) => { if(!value.includes("user")){ callback(new Error("不能删除基本 user 角色")); }else{ callback(); } } const roleRule = reactive({ roleCodes: [{ required: true, validator: containsUser, trigger: "change"}], }); const roleEditForm = reactive({ roleCodes:[], roleOptions:[], }) getRoleOptions(); async function getRoleOptions(){ try{ const result = (await HttpManager.getAllRoleWithOutSuperAdmin()) as ResponseBody; roleEditForm.roleOptions = result.data; (proxy as any).$message({ message: result.message, type: result.type, }); if (result.success) getData(); }catch(error){ console.error(error); } } function changeRole(row){ editRoleCode.value = row.id; roleEditForm.roleCodes = row.roleCodes; roleVisible.value = true; } async function saveRoleEdit() { try { let params = { userId: editRoleCode.value, codeList: roleEditForm.roleCodes } const result = (await HttpManager.changeRole(params)) as ResponseBody; (proxy as any).$message({ message: result.message, type: result.type, }); if (result.success) getData(); roleVisible.value = false; } catch (error) { console.error(error); } }
后端 ConsumerController 代码:
@PostMapping("user/changeRole") public Object changeRole(@RequestBody UserRoleParam param){ String userId = param.getUserId(); List<String> codeList = param.getCodeList(); // 检查是否删除了 user 角色 if(!codeList.contains(RoleCodeEnum.USER.getValue())){ return new ErrorMessage("不允许删除基本的 user 角色").getMessage(); } // 获取全部用户角色 ListRolesDto listRolesDto = new ListRolesDto(); listRolesDto.setNamespace(authenticationClient.getOptions().getAppId()); // 调用 authing 接口 RolePaginatedRespDto rolePaginatedRespDto = managementClient.listRoles(listRolesDto); List<RoleDto> roleDtoList = rolePaginatedRespDto.getData().getList(); // 设置清空和添加的共同目标 —— 用户 List<TargetDto> target = new ArrayList<>(); TargetDto targetDto = new TargetDto(); targetDto.setTargetType(TargetDto.TargetType.USER); targetDto.setTargetIdentifier(userId); target.add(targetDto); // 清空该用户角色 for(RoleDto roleDto:roleDtoList){ RevokeRoleDto revokeRoleDto = new RevokeRoleDto(); revokeRoleDto.setTargets(target); revokeRoleDto.setNamespace(authenticationClient.getOptions() .getAppId()); // code 不同 revokeRoleDto.setCode(roleDto.getCode()); // 调用 authing 接口 IsSuccessRespDto isSuccessRespDto = managementClient.revokeRole(revokeRoleDto); if(isSuccessRespDto.getStatusCode() != 200){ return new ErrorMessage("修改角色失败").getMessage(); } } // 添加角色 for(String code:codeList){ AssignRoleDto assignRoleDto = new AssignRoleDto(); assignRoleDto.setTargets(target); assignRoleDto.setNamespace(authenticationClient.getOptions() .getAppId()); assignRoleDto.setCode(code); // 调用 authing 接口 IsSuccessRespDto isSuccessRespDto = managementClient.assignRole(assignRoleDto); if(isSuccessRespDto.getStatusCode() != 200){ return new ErrorMessage("修改角色失败").getMessage(); } } return new SuccessMessage<>("修改成功").getMessage(); }

资源管理与授权

首先,介绍 Authing 定义的资源:

Authing 中的资源是你的业务系统中实际资源的标识符,一个资源可以是你的业务系统中的某一个实体,如 Order,我们也可以对 order 设定一个具体的操作,如:order:read;资源也可以是 UI 界面上的某一个菜单,一个按钮。资源的具体定义可以参考文档,当你把所有业务资源都在 Authing 中创建以后,就能控制对它们的访问、修改等权限。

如何在 Authing 控制台创建资源:

此处以及以下截图为旧版控制台
authing

记得权限分组选中自己创建的应用。

弹窗如下:
authing
1. 文件
你可以自定义资源,此处文件资源示例为歌单,定义如下:
authing
之后,可以对该资源进行授权,点击资源管理旁边的授权管理,再点击授权按钮。
authing
如图所示,只授权 user 角色访问 SongListIds 为 1、3、5、7、9 (Authing 控制台中要以下划线分割)的歌单(或者拒绝其访问偶数 id 的歌单),授权 vip 角色访问全部歌单。同理可以授权其他资源给用户、角色等……
authing
仅拥有 user 角色的用户访问 2 号歌单效果图:
authing
访问 3 号歌单,不会报错,且能看到数据:
authing
后端 ListSongController 代码:
@RequestMapping(value = "/listSong/detail", method = RequestMethod.GET) public Object listSongOfSongId(HttpServletRequest req, HttpSession session) { String songListId = req.getParameter("songListId"); // 获取歌单的 ids List<String> resources = getResources(session); if (resources.contains("*") || resources.contains(songListId)) { return new SuccessMessage<List<ListSong>>("添加成功", listSongService.listSongOfSongId(Integer.parseInt(songListId))) .getMessage(); } return new ErrorMessage("抱歉, 非 VIP 用户无法享用 VIP 歌单~").getMessage(); }
具体获取资源代码:
/** * 获取资源列表 * @param session * @return */ private List<String> getResources(HttpSession session) { GetUserRolesDto getUserRolesDto = new GetUserRolesDto(); getUserRolesDto.setNamespace(namespace); getUserRolesDto.setUserId(session.getAttribute("username").toString()); getUserRolesDto.setUserIdType(ResignUserReqDto.UserIdType.USERNAME.getValue()); // 调用 authing 接口 (获取用户的角色) RolePaginatedRespDto userRoles = managementClient.getUserRoles(getUserRolesDto); List<RoleDto> roles = userRoles.getData().getList(); List<String> resources = new ArrayList<>(); roles.forEach((dto) -> { GetRoleAuthorizedResourcesDto resourcesDto = new GetRoleAuthorizedResourcesDto(); resourcesDto.setNamespace(namespace); resourcesDto.setCode(dto.getCode()); resourcesDto.setResourceType(ResourceItemDto.ResourceType.DATA.getValue()); // 调用 authing 接口 (获取角色的资源) RoleAuthorizedResourcePaginatedRespDto roleAuthorizedResources = managementClient.getRoleAuthorizedResources(resourcesDto); List<RoleAuthorizedResourcesRespDto> list = roleAuthorizedResources.getData().getList(); List<String> resource = new ArrayList<>(); // 对资源做处理,获取歌单 ID list.stream().forEach(item -> { String[] ids = item.getResourceCode().split(":")[1].split("_"); resource.addAll(Arrays.asList(ids)); }); resources.addAll(resource); }); return resources; }
To create a perfect identity system
Online
How do you create a complete identity system?
Communicate Now
authing
Add Wecom to receive industry information
authing
authing
Download the Authing token and experience fast login authentication!
Free Trial
Online
Phone