栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

SDU信息门户(2)图灵认证授权子系统:用户登录

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

SDU信息门户(2)图灵认证授权子系统:用户登录

2021SC@SDUSC

目录

一.引言

1.传统登录方式弊端

2.OAuth 系统设计简介

      OAuth 协议概述:

二.用户登录部分代码分析

1.proto

2.application

(1)commands

(2)图灵认证系统的环境配置

(3)queries

3.domain

   models

4.infrastructure

从mangoDB中查询用户信息

将用户信息存储在mangoDB

三.总结


一.引言

1.传统登录方式弊端

       在传统应用中,如果想要拿到用户信息,第三方应用往往通过用户名 username 与密码 password 直接向用户服务器获取,此种方式将导致用户数据不安全,山东大学官方系统自前年起也通过 CAS 系统解决了这一系列问题。

2.OAuth 系统设计简介

          OAuth 是一个开放协议标准,协议标准为 RFC 6749。

      OAuth 协议概述:

       OAuth 分为若干种授权方式,最主要的是授权码方式与刷新令牌方式。
       用户访问第三方客户端应用后,第三方客户端请求用户授权,跳转至 OAuth 系统授权端点,用户同意授权后将获得授权码,用户将 OAuth 系统提供的授权码返回给第三方客户端应用,第三方应用凭借授权码去令牌端点换取获取令牌 AccessToken 或刷新令牌 Refresh Token。之后,第三方客户端应用拿到令牌,想要获取用户信息时通过 Access Token 获取即可。因为 用户信息较为敏感,所以 Access Token 失效时间十分短暂,因此需要通过 Refresh Token在系统中刷新 Access Token 以减少系统被攻击的可能性。

二.用户登录部分代码分析

1.proto

proto部分确定客户端服务相关方法:CreateClient(创建用户),FindById(ClientById) (根据用户ID寻找用户),ValidateClient(用户授权)

syntax = "proto3";

package turing.connect.client.v1;

message CreateClientReq {
  string name = 1;
  string logoUri = 2;
  repeated string scopes = 3;
  repeated string redirectUris = 4;
}

message CreateClientRsp {
  string id = 1;
  string secret = 2;
}

message ClientById {
  string id = 1;
}

message ClientData {
  string id = 1;
  string name = 2;
  string logoUri = 3;
  repeated string scopes = 4;
  repeated string redirectUris = 5;
}

message ValidateClientReq {
  string id = 1;
  string secret = 2;
}

message ValidateClientRsp {
  bool valid = 1;
  optional string error = 2;
}

// 客户端服务
service ClientService {
  rpc CreateClient(CreateClientReq) returns (CreateClientRsp);
  rpc FindById(ClientById) returns (ClientData);
  rpc ValidateClient(ValidateClientReq) returns (ValidateClientRsp);
}

2.application

(1)commands

实现@nestjs/cqrs中的ICommand接口

创建用户结构体并export:结构体的属性是只读的,已经写死,防止被更改

import { ICommand } from '@nestjs/cqrs';
import { AuthScope } from 'src/domain/models/auth-scope';

export class CreateClientCommand implements ICommand {
  constructor(
    public readonly name: string,
    public readonly logoUri: string,
    public readonly scopes: AuthScope[],
    public readonly redirectUris: string[],
  ) {}
}

返回前台送来的用户ID和用户密码创建用户并且异步操作

@CommandHandler(CreateClientCommand)
export class CreateClientHandler
  implements ICommandHandler
{
  constructor(
    @ClientRepositoryImplement()
    private readonly repository: ClientRepository,
  ) {}

  async execute(command: CreateClientCommand): Promise {
    const id = this.repository.nextId();
    const client = new Client({
      id,
      secret: '',
      name: command.name,
      logoUri: command.logoUri,
      scopes: command.scopes,
      redirectUris: command.redirectUris,
    });
    const secret = client.newSecret();
    client.create();
    await this.repository.save(client);
    client.commit();

    return {
      id,
      secret,
    };
  }
}

进行用户授权:

(1)先创建用户授权结构体再export

export class ValidateClientCommand implements ICommand {
  constructor(public readonly id: ClientId, public readonly secret: string) {}
}

 (2)进行用户授权确认:

@CommandHandler(ValidateClientCommand)
export class ValidateClientHandler
  implements ICommandHandler
{
  constructor(
    @ClientRepositoryImplement()
    private readonly repository: ClientRepository,
  ) {}

  async execute(command: ValidateClientCommand): Promise {
    const client = await this.repository.findById(command.id);
    if (!client) {
      throw new ClientNotFoundException();
    }
    if (!client.validate(command.secret)) {
      throw new GrpcException(status.INVALID_ARGUMENT, '客户端密钥错误');
    }
    return;
  }
}

如果用户在库中找不到,抛出异常ClientNotFoundException

import { GrpcException } from '@sdu-turing/microservices';
import { status } from 'grpc';

export class ClientNotFoundException extends GrpcException {
  constructor() {
    super(status.NOT_FOUND, '客户端不存在');
  }
}

如果用户密钥错误,抛出异常

import { GrpcException } from '@sdu-turing/microservices';
import { status } from 'grpc';

export class InvalidArgumentException extends GrpcException {
  constructor() {
    super(status.INVALID_ARGUMENT, '参数非法');
  }
}

(2)图灵认证系统的环境配置
import { IsEnum, IsString, IsUrl } from 'class-validator';
import { Env } from '@sdu-turing/config';

export enum NodeEnvironment {
  Development = 'development',
  Production = 'production',
  Test = 'test',
  Provision = 'provision',
}

export class AppConfigSchema {
  @IsEnum(NodeEnvironment)
  NODE_ENV: NodeEnvironment;

  @IsString()
  MONGO_URI: string;
}

export class AppConfig {
  mongoUri: string;

  nodeEnv: NodeEnvironment;

  constructor(@Env() env: AppConfigSchema) {
    this.nodeEnv = env.NODE_ENV;
    this.mongoUri = env.MONGO_URI;
  }
}

(3)queries

用户数据接口

import { AuthScope } from 'src/domain/models/auth-scope';

export interface ClientData {
  id: string;
  name: string;
  logoUri: string;
  scopes: AuthScope[];
  redirectUris: string[];
}

通过IDquery找到用户

import { IQuery } from '@nestjs/cqrs';

export class FindClientByIdQuery implements IQuery {
  constructor(public readonly clientId: string) {}
}

 将{ IQueryHandler, QueryHandler } 通过 '@nestjs/cqrs'导出

通过this.clientQuery.findById返回clientData

nestjs/cqrs简介:CQRS的核心除了Command与Query的分离,还有Controller层与Handler层的解耦。以往的MVC架构中,Controller层会实例化Service,比如UserService,CommentService。Service实例提供了数据库操作逻辑。 这就是Controller与Service的紧耦合。 NestJS的CQRS框架通过QueryBus/CommandBus(.net的CQRS框架中称为Mediator)实现了Controller与事件处理服务的解耦。

@QueryHandler(FindClientByIdQuery)
export class FindClientByIdHandler
  implements IQueryHandler
{
  constructor(
    @ClientQueryImplement()
    private readonly clientQuery: ClientQuery,
  ) {}

  async execute(query: FindClientByIdQuery): Promise {
    const clientData = await this.clientQuery.findById(query.clientId);
    return clientData;
  }
}

3.domain

   models

用户属性的model

import { AuthScope } from './auth-scope';
import { ClientId } from './client-id';

export interface ClientProperties {
  id: ClientId;
  name: string;
  secret: string;
  logoUri: string;
  scopes: AuthScope[];
  redirectUris: string[];
}

整个用户的model,包括各种用户方法。

import { AuthScope } from './auth-scope';
import { ClientId } from './client-id';
import { ClientProperties } from './client-properties';
import * as SHA256 from 'sha256';
import { nanoid } from 'nanoid';
import { AggregateRoot } from '@nestjs/cqrs';
import { ClientCreatedEvent } from '../events/client-created.event';

export class Client extends AggregateRoot {
  private _id: ClientId;

  private _name: string;

  private _secret: string;

  private _logoUri: string;

  private _scopes: AuthScope[];

  private _redirectUris: string[];

  constructor(props: ClientProperties) {
    super();
    this.assignProps(props);
  }

  get asProps(): ClientProperties {
    return {
      id: this._id,
      name: this._name,
      secret: this._secret,
      logoUri: this._logoUri,
      scopes: this._scopes,
      redirectUris: this._redirectUris,
    };
  }

  private assignProps(props: ClientProperties): this {
    this._id = props.id;
    this._name = props.name;
    this._secret = props.secret;
    this._logoUri = props.logoUri;
    this._redirectUris = props.redirectUris;
    this._scopes = props.scopes;
    return this;
  }

  create() {
    this.apply(new ClientCreatedEvent(this.asProps));
  }

  newSecret() {
    const secret = nanoid(32);
    this._secret = this.hashSecret(secret);
    return secret;
  }

  validate(secret: string) {
    return this._secret === this.hashSecret(secret);
  }

  private hashSecret(plain: string) {
    return SHA256(plain);
  }
}

4.infrastructure

从mangoDB中查询用户信息
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { ClientData } from 'src/application/queries/client-data.interface';
import { ClientQuery } from 'src/application/queries/client.query';
import { ClientCollection, Clientdocument } from './client.schema';

export class MongoClientQuery implements ClientQuery {
  constructor(
    @InjectModel(ClientCollection)
    private readonly clientModel: Model,
  ) {}

  async findById(id: string): Promise {
    const client = await this.clientModel.findById(id);
    if (!client) {
      return undefined;
    }
    return {
      id: client._id,
      name: client.name,
      logoUri: client.logoUri,
      scopes: client.scopes,
      redirectUris: client.redirectUris,
    };
  }
}

将用户信息存储在mangoDB
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { nanoid } from 'nanoid';
import { Client } from 'src/domain/models/client';
import { ClientId } from 'src/domain/models/client-id';
import { ClientRepository } from 'src/domain/models/client-repository';
import { ClientCollection, Clientdocument } from './client.schema';

export class MongoClientRepository implements ClientRepository {
  constructor(
    @InjectModel(ClientCollection)
    private readonly clientModel: Model,
  ) {}

  nextId(): ClientId {
    return new ClientId(nanoid(24));
  }

  async findById(id: ClientId): Promise {
    const clientDoc = await this.clientModel.findById(id.toString());
    if (!clientDoc) {
      return undefined;
    }
    return this.documentToModel(clientDoc);
  }

  async save(client: Client): Promise {
    const clientDoc = this.modelTodocument(client);
    await this.clientModel.updateOne(
      {
        _id: clientDoc._id,
      },
      clientDoc,
      {
        upsert: true,
      },
    );
  }

  private documentToModel(document: Clientdocument) {
    const props = document.toObject();
    return new Client({
      ...props,
      id: new ClientId(props._id),
    });
  }

  private modelTodocument(model: Client) {
    const props = model.asProps;
    const document = new this.clientModel({
      ...props,
      _id: props.id.toString(),
    });
    return document;
  }
}

三.总结

       本周主要通过学习SDU信息门户代码的图灵式的Oauth登录,学习掌握了Typescript语言,我之前从未接触typescript语言,现在已经略微学习到了基本的语法,通过分析了项目代码,更加深入地掌握理解了typescript,并且我也学习了一部分地nestJS,掌握了docker的使用方式以及如何使用docker来部署项目或者pull官方软件,还学会了写自己的docker-compose.yaml文件,对了,go语言的基本用法我也基本掌握了。虽然学习了很多新的知识,但感觉还有很多东西需要学习,学得越多,越感觉自己知识地浅陋。希望以后通过和队友的交流和自己的学习能学更多的知识。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/301459.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号