2021SC@SDUSC
catalogue
I. Introduction
In SDU information portal system, user authorization is an important content. Each user accessing the system is granted corresponding roles and specific permissions, so that the access permissions of different users are limited. They can access the content within their permissions and are not allowed to access the content outside their permissions.
The system is divided into several modules, each module has its own authorization system. This analysis is the authorization system of the educational administration system.
II. Code analysis
Idea: the user will first use the user name and password for authentication. Once authenticated, the server issues JWT, the JWT It can be used as an authorization in the authorization header of subsequent requests A token is sent to authenticate. We will also create a protected route that is only valid for the containing JWT Your request is accessible.
Configure @ Roles decorator
By configuring the @ Roles decorator, we can specify which Roles are allowed by the method by using @ Roles before the method.
Attach metadata to the route by using the @ SetMetadata() decorator provided by Nest as a custom decorator. To use SetMetadata, import it from @ nestjs/common.
import { SetMetadata } from '@nestjs/common'; export const Roles = (...roles: string[]) => SetMetadata('roles', roles);
After configuring the @ Roles decorator, you can import it into the file you are using.
import { Roles } from './decorators/roles.decorator';
Create JwtAuthGuard guard
import { ExecutionContext, ForbiddenException, Injectable } from '@nestjs/common'; import { Reflector } from '@nestjs/core'; import { AuthGuard } from '@nestjs/passport'; import { Observable } from 'rxjs'; @Injectable() export class JwtAuthGuard extends AuthGuard('jwt') { constructor( private reflector:Reflector ) { super(); } canActivate( context:ExecutionContext ): boolean | Promise<boolean> | Observable<boolean> { return super.canActivate(context); } handleRequest(err, user, info, context, status) { const roles = this.reflector.get<string[]>("roles", context.getHandler()); if( !roles ) return user; const result = this.matchRoles(roles, user.usertype); if( result ) return user; else throw new ForbiddenException("Permission error, illegal access!"); } private matchRoles(roles:string[], usertype:string): boolean { for(let index=0; index<roles.length; index++) if( roles[index] == usertype ) return true; //If foreach() is used for the same logic, an error is reported return false; } }
Configure TokenSchema
In the token.schema.ts file, the TokenSchema is obtained by importing the Schema from mongoose, calling the constructor of the Schema and passing parameters, which stores the user's id, token, role and other information.
import { Schema } from "mongoose"; export const TokenSchema = new Schema({ //Job number or student number id: String, //token token: String, //Type (academic teacher, student?) type: String })
Configure login routing
After using the @ Controller decorator, the AuthController class becomes a Controller, which is used to receive and process related requests (routes). Where @ Post and @ Get are the request types. The authService type object is passed in the constructor. Automatic dependency injection is used here, that is, automatically create and assign a data member of the same type with the same name as authService. In addition, the class defines the login and getProfile methods.
import { Controller, Get, Post, UseGuards, Request} from '@nestjs/common'; import { AuthService } from './auth.service'; import { Roles } from './decorators/roles.decorator'; import { JwtAuthGuard } from './guards/jwt-auth.guard'; import { ApiTags } from '@nestjs/swagger'; @ApiTags("Authentication system") @Controller('auth') export class AuthController { constructor( private authService:AuthService ){} @Post("login") async login() { return this.authService.login(); } @UseGuards(JwtAuthGuard) @Get("profile") async getProfile(@Request() req) { return req.user; } }
The login method handles the / auth/login route. The login method of authService is executed internally to handle user login.
Before the getProfile method, the @ UseGuards() decorator is used to bind the guard. The JwtAuthGuard guard is bound to the getProfile method for authentication. The method can be executed only after the identity passes. The getProfile method handles / auth/profile route is used to obtain the identity interface after login.
Generate JWT
@nestjs/jwt is a utility package that helps jwt operate. Here, we create a service class of AuthService and inject two data members, tokenModel of Model type and jwtservice of jwtservice type, through the constructor. Three methods are defined -- login, validateUser and generateToken.
import { Injectable } from '@nestjs/common'; import { InjectModel } from '@nestjs/mongoose'; import { Model } from 'mongoose'; import { JwtService } from "@nestjs/jwt" @Injectable() export class AuthService { constructor( @InjectModel("adminToken") private tokenModel:Model<any>, private jwtService:JwtService ) {} async login() { /* if( !token ){//If the user is not logged in //Redirect to the Oauth system to obtain idtoken and typetoken //Perform the login operation after returning return; } //User submits idtoken and typetoken //The system obtains the user id and type from the Oauth system //Regenerate into a Mytoken of the system //Store {id, Mytoken, type} in tokens //Return to user Mytoken*/ return this.generateToken("a0001", "admin"); } async validateUser( token:string ) :Promise<string> { let query = this.tokenModel.find(); query.where({token: token}); query.setOptions({lean:true}); const result = await query.findOne().exec(); return result.type; } private async generateToken( userid:string, usertype:string ) { const payload = { id:userid, type:usertype }; return { access_token: this.jwtService.sign(payload), }; } }
login method: called when the user logs in. The return value is the return value of the generateToken method.
generateToken method: generate a payload object according to the passed in user id and user type parameters, encrypt the user information in the payload object through the sign method of jwtService, and the returned value after encryption is used as the user's token (Jwt).
validateUser method: the purpose of this method is to retrieve the user and verify the password, retrieve the user list through the find method of tokenModel, find the user information matching the token, and then verify the password through the findOne() method. The return value is the type of verification result.