在这个 login 方法中, decodeUserFromToken 封装了 @auth0/angular2-jwt 中提供的 decodeToken 方法,注意 decodeToken 方法解析出来的只是服务端 jsonwebtoken.sign() 中的JSON对象,所以需要通过 . 操作获取 jsonwebtoken.sign() 中定义的 user :
decodeUserFromToken(token): User { return this.jwtHelperService.decodeToken(token).user; }
在这个服务中,定义了两个变量 loggedIn 和 isAdmin ,用来标识用户是否登录和其相应的权限,方便在Angular路由中控制可以访问的视图。
有登录当然就有登出,登出时只需把token从LocalStorage中移除,并把几个变量重置即可:
logout(): void { this.storageService.removeLocalStorage('token'); this.loggedIn = false; this.isAdmin = false; this.currentUser = new User(); }
AuthService 的完整代码如下:
import { Injectable, Injector } from '@angular/core'; import { Router } from '@angular/router'; import { JwtHelperService } from '@auth0/angular-jwt'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; import { LoginInfo, LoginRes, User } from '../../views/passport/interfaces/passport'; import { PassportService } from '../../views/passport/services/passport.service'; import { StorageService } from '../storage/storage.service'; import { NzMessageService } from 'ng-zorro-antd'; @Injectable() export class AuthService { public loggedIn = false; public isAdmin = false; public currentUser: User = new User(); constructor(private jwtHelperService: JwtHelperService, private router: Router, private injector: Injector, private passportService: PassportService, private storageService: StorageService) { const token = localStorage.getItem('token'); if (token) { const decodedUser = this.decodeUserFromToken(token); this.setCurrentUser(decodedUser); } } get msg(): NzMessageService { return this.injector.get(NzMessageService); } login(loginInfo: LoginInfo): Observable<boolean> { return this.passportService.postLogin(loginInfo).pipe(map( (res: LoginRes) => { this.storageService.setLocalStorage('token', res.token); const decodedUser = this.decodeUserFromToken(res.token); this.setCurrentUser(decodedUser); this.msg.success('登录成功!'); return this.loggedIn; } ) ); } logout(): void { this.storageService.removeLocalStorage('token'); this.loggedIn = false; this.isAdmin = false; this.currentUser = new User(); } decodeUserFromToken(token): User { return this.jwtHelperService.decodeToken(token).user; } setCurrentUser(decodedUser): void { this.loggedIn = true; this.currentUser.workNumber = decodedUser.workNumber; this.currentUser.realName = decodedUser.realName; this.currentUser.group = decodedUser.group; this.currentUser.role = decodedUser.role; this.isAdmin = decodedUser.role > 10; delete decodedUser.role; } }
至此,在你的Angular应用中就引入了JWT认证,当然,你也可以不使用 @auth0/angular2-jwt ,自己手写一个HTTP拦截器,手动设置每次请求的header:
@Injectable() export class AuthInterceptor implements HttpInterceptor { intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { const token = localStorage.getItem("token"); if (token) { const cloned = req.clone({ headers: req.headers.set("Authorization", "Bearer " + token) }); return next.handle(cloned); } else { return next.handle(req); } } }
不过这样的话,token Base64解码也需要自己手写,稍微麻烦一点。
总结
JWT因为是基于JSON的,所以通用性很强,很多语言已经存在jwt相关的库。不过使用JWT的时候需要注意以下几点:
保存好secret秘钥,这个秘钥只能在服务端存在
给token设置一个过期时间,因为一旦token生成,它就永远有效,除非token密钥被更改或过期
在payload中只能存储一些业务逻辑所必要的非敏感信息