Security

认证和授权

认证与授权组件是@celljs/security。Cell.js 框架结合传统后台管理系统和云计算平台的认证与授权理念,并借鉴了 Spring Security 设计思想,抽象了一套通用的认证与授权模型。Spring Security 对前后端分离架构支持不是特别友好,Cell.js 框架在这方面也做了不少改进。

认证与授权组件是@celljs/security。Cell.js 框架结合传统后台管理系统和云计算平台的认证与授权理念,并借鉴了 Spring Security 设计思想,抽象了一套通用的认证与授权模型。Spring Security 对前后端分离架构支持不是特别友好,Cell.js 框架在这方面也做了不少改进。

框架提供的认证与授权默认实现比较简单,认证是基于用户名与密码方式;授权是授权装饰器 + EL 表达式。但是,框架提供了很多扩展点,基于这些扩展点,我们可以实现各种各样复杂的认证与授权实现,例如@celljs/oauth2-client组件是基于@celljs/security组件扩展实现,提供了基于 ODIC 的认证和 OAuth2.0 的授权能力。

使用方法

框架提供了一个开箱即用的组件@celljs/security,该组件提供了一套默认的行为规则,例如

  • 默认的登录页面路由
  • 默认的登录提交路由
  • 默认的密码 Hash 算法
  • 默认的登录成功页面路由
  • 默认的登出成功页面路由
  • 默认的用户服务接口实现,从内存中加载用户信息。等等

大部分默认行为,我们无需自定义。有少量是需要我们根据业务情况自己实现的,比如登录页面、登录成功页面、登出成功页面,以及用户服务接口的实现(从数据库中加载用户信息)。安装 @celljs/security 组件如下:

npm install @celljs/security

实现登录页面

在登录页面中,默认需要通过 POST 方法(可以自定义),并且携带用户名(username)和密码(password)信息发送请求到 /login ,该动作将触发认证流程。

默认提供的内置用户信息:用户名/密码为 admin/123456(我们也可以实现用户服务接口 UserService ,提供真实业务的用户信息)。在后端,当用户名和密码认证成功,则跳转到登录成功页面,默认登录成功页面路由是 / ,否则,跳转到登录页面 /login

接口与页面保护

当我们添加了该组件并添加 @Authenticated 装饰器后,默认对外的接口方法和页面都会被保护起来,需要登录以后才能访问接口方法。我们也可以给接口方法配置需要拥有什么样的权限才能访问,该组件默认提供了 @PreAuthorize  装饰器,定义接口方法需要什么样的权限才能够被访问。

@Get()
@Authenticated()
@PreAuthorize('hasRole("admin")')  // 表示当前登录的用户需要拥有管理员角色才可访问
@Transactional({ readOnly: true })
list(): Promise<User[]> {
  const repo = OrmContext.getRepository(User);
  return repo.find();
}

@PreAuthorize这个装饰器中的参数是一个 el 表达式字符串,只要 el 表达式上下文中有的属性、方法,都可以在 el 表达式中使用,目前可以通过框架提供的接口扩展 el 表达式上下文中的属性和方法,hasRole 目前并没有,这里只是举例。如果开发者需要的话,可以自行注册 hasRole 为 el 表达式上下文中的一个方法。

当没有登录的时候,通过 ajax 直接访问接口,将返回 401 状态码;通过浏览器访问页面,将返回 302 状态码,重定向到登录页面,登录成功后,又会重定向到最开始要访问的页面。

登录成功后,当我们有权限访问该方法或页面,则访问成功,否则,访问失败,返回 403 状态码。

匿名访问

方法上添加装饰器 @Anonymous ,可以让方法可以匿名访问。

@Get()
@Anonymous()
@Transactional({ readOnly: true })
list(): Promise<User[]> {
  const repo = OrmContext.getRepository(User);
    return repo.find();
}

也可以添加到类上,让类的所有方法可以里面访问。

@Controller('users')
@Anonymous()
export class UserController {
    ...
}

鉴权访问

方法上添加装饰器 @Authenticated ,可以让方法可以需要鉴权才能访问。

@Get()
@Authenticated()
@Transactional({ readOnly: true })
list(): Promise<User[]> {
  const repo = OrmContext.getRepository(User);
    return repo.find();
}

也可以添加到类上,让类的所有方法需要鉴权才能访问。

@Controller('users')
@Authenticated()
export class UserController {
    ...
}

OAuth

Cell.js 框架提供 OAuth 支持,安装 @celljs/oauth-client 配置对应的 providersregistrations 即可使用 OAuth 功能。

GitHub 在线示例 https://github.com/cellbang/cell/blob/master/examples/accounts

Dex OAuth 示例

在 Dex 中配置授权 App。

# cell.yml
staticClients:
  - id: demo-app
    secret: "123456"
    name: "Demo App"
    redirectURIs:
      - "http://localhost:3000/login/oauth2/code/dex"

查看本地 Dex 服务配置。

curl http://localhost:5556/dex/.well-known/openid-configuration

在 Cell.js 项目中配置名为 dex 的认证提供者。

# cell.yml
cell:
  oauth2:
    client:
      providers:
        dex:
          authorizationUri: http://127.0.0.1:5556/dex/auth
          tokenUri: http://127.0.0.1:5556/dex/token
          userInfoEndpoint:
            uri: http://127.0.0.1:5556/dex/userinfo
            userNameAttributeName: name
          jwkSetUri: http://127.0.0.1:5556/dex/keys
          issuerUri: http://127.0.0.1:5556/dex

在 Cell.js 项目中配置名为 dex 的认证服务。

# cell.yml
cell:
  oauth2:
    client:
      registrations:
        dex:
          clientName: Dex
          clientId: ${ 'demo-app' | onTarget('backend') } # 请将 demo-app 替换为对应的 clientId
          clientSecret: ${ '123456' | onTarget('backend') } # 请将 123456 替换为对应的 clientSecret
          scopes: [openid, profile, groups]
          redirectUri: ${cell.oauth2.client.defaultRedirectUri}
          authorizationGrantType: authorization_code
          clientAuthenticationMethod: basic

启动 Cell.js 项目,在浏览器中访问 http://localhost:3000/login/oauth2/code/dex 地址即可使用认证功能,cell.oauth2.client.registrations.dex.redirectUri 为 OAuth 应用认证后的跳转地址,dex 服务对应的跳转地址为 /login/oauth2/code/dex

Dex 参考 https://dexidp.io/docs/using-dex/


Copyright © 2024 Zero (github@groupguanfang) 粤ICP备2023102563号