最近正在使用Angular开发Ai-Admin快速开发平台,UI框架选择了Ant Design的ng-zorro,登录认证我们使用token。
完整代码:https://gitee.com/chou-xf/ai-admin
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) => string | null | Promise<string | null>;
headerName?: string;
authScheme?: string | ((request?: HttpRequest) => 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中最好不要有敏感信息以免泄露。
完整代码:https://gitee.com/chou-xf/ai-admin