用户登陆(前后台)

在看完后台登陆操作以后对登陆整个流程有了更深的理解,个人认为有必要总结一下。
下面是整个登陆操作的流程图:

我们首先从前台开始:

onSubmit() {
    const username = this.formGroup.get('username').value;
    const password = this.formGroup.get('password').value;
    this.teacherService.login(username, password).subscribe(result => {
      console.log(result);
      if (result) {
        this.teacherService.setIsLogin(true);
      } else {
        console.log('用户名密码错误');
      }
    });
  }

 用户在V层输入信息传到C层再调用M层代码

  login(username: string, password: string): Observable<boolean> {
    const url = 'http://localhost:8080/Teacher/login';
    return this.httpClient.post<boolean>(url, {username, password});
  }

  setIsLogin(isLogin: boolean) {
    window.sessionStorage.setItem(this.isLoginCacheKey, this.convertBooleanToString(isLogin));
    this.isLogin.next(isLogin);
  }

 

如果后台返回信息为true则设置缓存isLogin为1;
如果后台返回信息为false则登陆失败。

前台通过post方法将username,password传给后台。
之后会被前台的拦截器拦截

export class AuthTokenInterceptor implements HttpInterceptor {
  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    const reqClone = req.clone({
      setHeaders: {'auth-token': CacheService.getAuthToken()}
    });
    ...
      return httpEvent;
    }));
  }
}
 private static authToken: string = sessionStorage.getItem('authToken');
 static getAuthToken() {
    if (CacheService.authToken === null) {
      return '';
    }
    return CacheService.authToken;
  }

 拦截器会设置request的header中的令牌通过sessionStorage缓存。

sessionStorage 用于临时保存同一窗口(或标签页)的数据,在关闭窗口或标签页之后将会删除这些数据。
 

之后数据会转发到后台。
首先会经过后台过滤器,检查request的header中的令牌是否有效,如果有效会设置响应的header中的令牌为该令牌,如果无效会通过UUID.randomUUID().toString()方法再生成一个令牌,并且将该令牌加入到缓存,通过装饰器模式将该令牌加入到请求的header中。之后将数据继续转发。

在这之中在将从请求的header获取中用到了装饰器模式

class HttpServletRequestTokenWrapper extends HttpServletRequestWrapper {
        HttpServletRequestTokenWrapper httpServletRequestTokenWrapper;
        String token;
        private HttpServletRequestTokenWrapper(HttpServletRequest request) {
            super(request); //super()可看作调用父类的构造函数
        }

        public HttpServletRequestTokenWrapper(HttpServletRequest request, String token) {
            this(request);
            this.token = token;
        }

        @Override
        public String getHeader(String name) {
            if(TOKEN_KEY.equals(name)) {
                logger.info("设置了token为" + token);
                return this.token;
            }
            return super.getHeader(name);
        }
    }

 

装饰器模式个人认为就是相当于对HttpServletRequest新写了一个子类,重写了构造函数使得可以将token传入对象中,再重写它的getheader方法,如果是用getheader获取令牌则返回通过构造函数传入的令牌。并且根据测试装饰器的作用范围不只是在过滤器中,而是适用于整个后台项目,即在其他文件方法中调用getheader("auth-token")也可以得到想要的结果。

数据继续转发后会经过后台拦截器,在这里判断用户是否登陆,如果未登录则会进行拦截并返还给前台401错误。

String url = request.getRequestURI();
        String method = request.getMethod();
        System.out.println("请求的地址为" + url + "请求的方法为:" + method);

        if( "OPTIONS".equals(method)) {
            /** 
            请求方法为OPTIONS,浏览器在进行跨域访问时,如果发现请求的方法不是get,那么在请
            求以前则会向该请求地址发送options方法来确认后台允许前台发起的请求方法,当后台返回
            的允许请求方法中包括了 POST方法时,浏览器才会发起请求,否则将放弃请求。
            **/
            return true;
        }

        // 判断请求地址、方法是否与用户登录相同
        if ("/Teacher/login".equals(url) && "POST".equals(method)) {
            System.out.println("请求地址方法匹配到登录地址,不拦截");
            return true;
        }

        // auth-token是否绑定了用户
        String authToken = request.getHeader(TokenFilter.TOKEN_KEY);
        if (this.teacherService.isLogin(authToken)) {
            System.out.println("当前token已绑定登录用户,不拦截");
            return true;
        }

        System.out.println("当前token未绑定登录用户,返回401");
        // 为响应加入提示:用户未登录
        response.setStatus(401);
        return false;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
                           @Nullable ModelAndView modelAndView) throws Exception {
        logger.info("执行拦截器postHandle");
    }

经过拦截器之后请求会被转发到后台的C层,匹配到相应的url和方法后执行对应的login操作。如果根据传来的username和password验证失败就会直接返回false,如果验证成功则会把当前请求包含的令牌与当前登陆用户的ID进行绑定。并返回true;

令牌与用户ID绑定:

this.authTokenTeacherIdHashMap.put(this.request.getHeader("auth-token"), teacher.getId());

 相应的,如果想要判断用户是否登陆只需要判断当前的token有没有与之绑定的用户id,即:

public boolean isLogin(String authToken) {
        Long teacherId = this.authTokenTeacherIdHashMap.get(authToken);
        return teacherId != null;
    }

 如果想要注销就只需要删除令牌与用户id的绑定:

public void logout() {
        String authToken = this.request.getHeader("auth-token");
        logger.info("获取到的auth-token为" + this.request.getHeader("auth-token"));
        this.authTokenTeacherIdHashMap.remove(authToken);
    }

 当后台处理完毕返回结果时还要经过后台拦截器的事后过滤,经过事后过滤一个完整的响应就已经完成了,在这之后还需要经过后台过滤器进行事后监控才会将响应传往前台。
响应到达前台后还要经过前台拦截器进行更新当前令牌的操作才能被继续转发。

const reqClone = req.clone({
      setHeaders: {'auth-token': CacheService.getAuthToken()}
    });
 return next.handle(reqClone).pipe(map((httpEvent) => {
      if (httpEvent instanceof HttpResponse) {
        const httpResponse = httpEvent as HttpResponse<any>;
        const authToken = httpResponse.headers.get('auth-token');
        console.log('拦截1'); //执行一次
        CacheService.setAuthToken(authToken);
      }
      console.log('拦截2'); //执行两次
      return httpEvent;
    }));

 这也证明了拦截器会对响应作处理,上面是执行一次请求的执行结果。
将响应的令牌存到缓存中即更新当前令牌:

static setAuthToken(token: string) {
    CacheService.authToken = token;
    sessionStorage.setItem('authToken', token);
  }

至此整个登陆流程介绍完毕。 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值