Angular中使用JWT

444 阅读3分钟

最近正在使用Angular开发Ai-Admin快速开发平台,UI框架选择了Ant Design的ng-zorro,登录认证我们使用token。

login.jpg

完整代码:gitee.com/chou-xf/ai-…

Session认证

由于http协议是无状态的,所以客户端每次访问都是新的请求。这样每次请求都需要验证身份,传统方式是用session+cookie来记录/传输用户信息。

由于session是保存在服务器里,所以如果分布式部署应用的话,会出现session不能共享的问题,很难扩展(可以通过Redis解决Session共享问题)。

什么是JWT

JWT(JSON WEB TOKEN):JSON网络令牌,JWT是一个轻便的安全跨平台传输格式,定义了一个紧凑的自包含的方式在不同实体之间安全传输信息(JSON格式)。它是在Web环境下两个实体之间传输数据的一项标准。实际上传输的就是一个字符串。广义上讲JWT是一个标准的名称;狭义上JWT指的就是用来传递的那个token字符串。

JWT是一种Token规范,主要面向的还是登录、验证和授权方向,当然也可以用只来传递信息。一般都是存在header里,也可以存在cookie里。

而JWT就是更安全方便的方式。它的特点就是简洁,紧凑和自包含,而且不占空间,传输速度快,而且有利于多端分离,接口的交互等等。

这里不在介绍如何在服务端生成JWT,直接讲如何在Angular客户端中使用JWT。

方案一:使用@auth0/angular2-jwt

在Angular中,我们可以使用angular2-jwt这个库来帮助处理JWT:

npm install @auth0/angular-jwt --save

然后在app.module.ts中引入JwtModule模块。

import { JwtModule } from '@auth0/angular-jwt';

export function tokenGetter() {
  return localStorage.getItem('token');
}

@NgModule({
  declarations: [
      ...
  ],
  imports: [
    ...
    JwtModule.forRoot({
      config: {
        tokenGetter: tokenGetter
      }
    })
  ],
  bootstrap: [AppComponent],
})
export class AppModule {}

JwtModlue中的JwtConfig:

export interface JwtConfig {
    tokenGetter?: (request?: HttpRequest<any>) => string | null | Promise<string | null>;
    headerName?: string;
    authScheme?: string | ((request?: HttpRequest<any>) => string);
    allowedDomains?: Array<string | RegExp>;
    disallowedRoutes?: Array<string | RegExp>;
    throwNoTokenError?: boolean;
    skipWhenExpired?: boolean;
}
  • tokenGetter:如何获取token,这里从localStorage中获取;
  • allowedDomains:允许发送认证请求的域名;
  • disallowedRoutes:不希望替换header中Authorization信息的api列表;
  • skipWhenExpired:token过期是否跳过;

创建AuthService实现登录、存储token、解析token(解析当前登录的用户信息)、保存当前登录的用户信息等。

export class AuthService {

  private loggedIn = false;

  constructor(
    private router: Router,
    private jwtHelperService: JwtHelperService,
    private storageService: StorageService
  ) {
    const token = localStorage.getItem('token');
    if (token) {
      this.loggedIn = true;
      const user = this.jwtHelperService.decodeToken(token).user;
      console.log(this.jwtHelperService.decodeToken(token));
      // parse token and set user info
    }
  }

  login() {
    this.loggedIn = true;
    this.storageService.setLocalStorage('token', token);
    this.router.navigateByUrl("");
  }

  logout() {
    this.loggedIn = false;
  }

  isLoggedIn() {
    return this.loggedIn;
  }
}
  • login方法用户发送登录请求,以及将登录成功后返回的token存储到LocalStorage中,这样在失效前无需再次登录。
  • loggedIn用于标记用户是否已经登录,方便结合路由守卫控制路由跳转。

相对登录,登出就简单多了,只需把token移除并充值变量即可。

logout(): void {
    this.storageService.removeLocalStorage('token');
    this.loggedIn = false;
    this.currentUser = new User();
}

方案二:手写Http拦截器

原理很简单,拦截Http请求并设置每次请求的header,也就是在header中添加token。

@Injectable()
export class AuthInterceptor implements HttpInterceptor {

    intercept(req: HttpRequest<any>,
              next: HttpHandler): Observable<HttpEvent<any>> {

        const token = localStorage.getItem("token");

        if (token) {
            const authReq = req.clone({
                headers: req.headers.set("Authorization",
                    "Bearer " + token)
            });

            return next.handle(authReq);
        }
        else {
            return next.handle(req);
            // 或者做替他处理,例如跳转到401页面等
        }
    }
}

这里更推荐方案一,毕竟是成熟的库。需要注意的是,token存储在前端最好加密或者token中最好不要有敏感信息以免泄露。

完整代码:gitee.com/chou-xf/ai-…