malagu认证与授权之@malagu/security
导言
- 本文已参与「开源摘星计划」,欢迎正在阅读的你加入。活动链接:https://github.com/weopenproj...
- malagu的认证与授权参考spring-security思想,详细介绍请移步官方文档。malagu除了基本的security外提供了ODIC 的认证和 OAuth2.0 的授权能力,本文主要介绍@malagu/security组件的基本应用实践。
认证与授权组件 @malagu/security的简单应用
1.添加组件在项目中引用@malagu/security组件
yarn add @malagu/security # 或者 npm i @malagu/security
2.重写loginUrl和logoutUrl定义登录和注销接口和请求方式
malagu: security: loginUrl: /api/login loginMethod: POST logoutUrl: /api/logout
3.重写UserService实现自定义登录注册用户时密码需要使用PasswordEncoder生成;我们只需将用户和密码在load()中赋值给security组件User即可,校验比对逻辑交由@malagu/security组件完成。
import { Component, Autowired } from '@malagu/core'; import { UserService, UsernameNotFoundError, AccountStatusError, PasswordEncoder } from '@malagu/security/lib/node'; import { User, ElPolicy, PolicyType, AuthorizeType } from '@malagu/security'; import { OrmContext, Transactional } from "@malagu/typeorm/lib/node"; import { UserEntity } from '@microservice/datasource'; /** * 重写UserService实现自定义登录 * @param username 登录名可以是用户名称(user_name)或者电话(mobile), 优先级:user_name > mobile */ @Component({ id: UserService, rebind: true }) export class UserServiceImpl implements UserService<string, User> { @Autowired(PasswordEncoder) protected readonly passwordEncoder: PasswordEncoder; @Transactional({ readOnly: true }) async load(username:string): Promise<User>{ const repo = OrmContext.getRepository(UserEntity); let user = await repo.findOne({ userName: username }) if (!user) { user = await repo.findOne({ mobile: username }) } if (!user) { throw new UsernameNotFoundError(); } if(user.state == false){ throw new AccountStatusError(); } return { type: "", username: user.userName, password: user.password, policies: [ <ElPolicy>{ type: PolicyType.el, authorizeType: AuthorizeType.Pre, el: 'true' } ], accountNonExpired: true, accountNonLocked: true, credentialsNonExpired: true, enabled: true } } }
4.重写认证失败处理器AuthenticationErrorHandler
import { Component, Autowired } from '@malagu/core'; import { HttpStatus } from '@malagu/web'; import { ErrorHandler, Context, RedirectStrategy } from '@malagu/web/lib/node'; import { AuthenticationErrorHandler, AUTHENTICATION_ERROR_HANDLER_PRIORITY, AuthenticationError } from '@malagu/security/lib/node' @Component({ id: AuthenticationErrorHandler, rebind: true }) export class AuthenticationErrorHandlerImpl implements ErrorHandler{ readonly priority: number = AUTHENTICATION_ERROR_HANDLER_PRIORITY; @Autowired(RedirectStrategy) protected readonly redirectStrategy: RedirectStrategy; canHandle(ctx: Context, err: Error): Promise<boolean> { return Promise.resolve(err instanceof AuthenticationError); } async handle(ctx: Context, err: AuthenticationError): Promise<void> { let message = ""; switch (err.name) { case "UsernameNotFoundError": ctx.response.statusCode = HttpStatus.FORBIDDEN; message = "用户不存在"; break; case "BadCredentialsError": ctx.response.statusCode = HttpStatus.FORBIDDEN; message = "用户密码错误"; break; case "AccountStatusError": ctx.response.statusCode = HttpStatus.FORBIDDEN; message = "用户被冻结"; break; case "AuthenticationError": ctx.response.statusCode = HttpStatus.UNAUTHORIZED; message = "用户没有访问权限,需要进行身份认证"; break; default: ctx.response.statusCode = HttpStatus.UNAUTHORIZED; message = err.message; break; } ctx.response.end(message); } }
5.重写认证成功处理器AuthenticationSuccessHandler非必需,不重写将跳转到首页
import { Component } from '@malagu/core'; import { HttpStatus } from '@malagu/web'; import { AuthenticationSuccessHandler, Authentication } from '@malagu/security/lib/node' import { Context } from '@malagu/web/lib/node' @Component({ id: AuthenticationSuccessHandler, rebind: true }) export class AuthenticationSuccessHandlerImpl implements AuthenticationSuccessHandler { async onAuthenticationSuccess(authentication: Authentication): Promise<void> { Context.getResponse().statusCode = HttpStatus.OK; Context.getResponse().body = JSON.stringify({ username: authentication.name }); } }
6.重新登出处理器LogoutSuccessHandler非必需,不重写将跳转到登录页
import { LogoutSuccessHandler, LOGOUT_SUCCESS_HANDLER_PRIORITY } from '@malagu/security/lib/node'; import { Component } from '@malagu/core'; import { HttpStatus } from '@malagu/web'; import { Context } from '@malagu/web/lib/node'; @Component({id: LogoutSuccessHandler, rebind: true }) export class LogoutSuccessHandlerImpl implements LogoutSuccessHandler { readonly priority = LOGOUT_SUCCESS_HANDLER_PRIORITY; async onLogoutSuccess(): Promise<void> { Context.getResponse().statusCode = HttpStatus.OK; Context.getResponse().body = "登出成功"; } }
7.@Authenticated的使用可以在controller类上使用,这样该类下的所有开放接口都需要鉴权
@Controller("user") @Authenticated() export class UserController { @Autowired(UserInfoService) protected userInfoService: UserInfoService; ... ... }
也可以在指定的接口上使用
@Get("/:userId") @Json() @Authenticated() async getUserInfo(@Param("userId") userId: number){ const result = await this.userInfoService.getUserInfo(userId); return result }
- 8.除了@Authenticated,malagu还提供了用于权限控制的装饰器@PreAuthorize以及匿名@Anonymous
结语
至此,@malagu/security的核心代码就已经完成。在module.ts文件引用,运行项目我们就可以进行的调试了。由于登录逻辑都交由组件处理了,malagu的认证授权还是比较简单的。
思考
- @malagu/security的原理是怎样的?
- UserService中返回体User各属性的含义?
- 登录有效期怎么设置?
- 可以使用@malagu/security实现单点登录吗?
本文为学习类文章,如有错误欢迎指正!思考内容欢迎各位大佬答疑解惑。
转载自:https://segmentfault.com/a/1190000042291255