乐趣区

关于typescript:malagu认证与授权之malagusecurity

导言

  • 本文已参加「开源摘星打算」,欢送正在浏览的你退出。流动链接: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 实现单点登录吗?

本文为学习类文章,如有谬误欢送斧正!思考内容欢送各位大佬答疑解惑。

退出移动版