SpringSecurity自定义登录接口 & Angular2的安全路由

在配置SpringSecurity的项目中,通过配置.authorizeRequests().antMathers("/path/**",....) 来设置哪些路径需要用户权限,哪些可以不登录浏览。但是如果在该项目中部署了Angular2单页应用的话,由于对SpringSecurity来说仅在第一次进入Angular2应用时经过了Security拦截,之后所有的页面跳转均在Angular2的路由管理下完成,因此SpringSecurity无法管理Angular2应用内部的页面权限,此时需要对Angular2的路由进行改造,让Angular2的路由实现类似Security的功能。

登录接口改造

SpringSecurity改造

默认情况下,SpringSecurity通过设置.formLogin() 来定义登录页入口,最后通过登录页的表单提交完成验证,并跳转至首页。在Angular2项目中,若登录页也由Angular2管理,则登录过程需要通过ajax调用接口实现。因此需要自定义一个登录接口完成登录页post表单登录的流程。
通过AuthenticationManager 即可调用内部的验证流程。
代码如下

@Autowired
AuthenticationManager authenticationManager;
@ResponseBody
@RequestMapping(value="doLogin",method = RequestMethod.POST)
public ResultBean doLogin(HttpServletRequest request, HttpServletResponse response,
            @RequestParam("userName")String userName,@RequestParam("password") String password){
        ResultBean bean = new ResultBean();
        try {
            // 内部登录请求
            UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(userName, password,AuthorityUtils.commaSeparatedStringToAuthorityList(""));
            // 验证
            Authentication auth = authenticationManager.authenticate(authRequest);
            SecurityContextHolder.getContext().setAuthentication(auth);
            bean.setReturnCode(1);
        } catch (AuthenticationException e) {   
            bean.setReturnCode(0);
            bean.setReturnMsg(e.getMessage());
        } 
        return bean;
    }

Angular2登录

在合适的位置编写登录函数,提交一个http请求即可。

auth(userName: string, password: string): void {
    let that = this;
    this.logger.info('[auth guard] start loging... userName:' + userName + ', password:' + password);
    //这里通过制定headers,避免数据以json格式提交
    this.headers = new Headers({ 'Content-Type': 'application/x-www-form-urlencoded' });
    //通过springsecurity的csrf保护
    this.headers.append(document.getElementsByName('_csrf_header')[0].getAttribute('content'), document.getElementsByName('_csrf')[0].getAttribute('content'));
    //登录过程
    this.http.post(this.appStorageService.getCtx() + '/doLogin', 'userName=' + userName + '&password=' + password, { headers: this.headers }).subscribe(function (res) {
      that.logger.info('[auth guard] login result');
      that.logger.info(res.toString());
    })
  }

以上就是SpringSecurity端和Angular2端的登录改造基础步骤。下面改造Angular2的路由,使其带有用户权限验证判断功能。

Angular2路由改造

Angular2/core 包中有一个CanActivate 接口可应用于路由中,判断路由是否可激活。我们设置一个SessionStorage存储用户的登录信息即可,若无登录信息则说明用户未登陆过,若有信息则说明用户登录过。这里亦可以进一步改造后端,使用Token形式存储,更为安全。

步骤1:创建验证服务,继承CanActivate接口

在这里实现登录权限判断,与登录方法。(这里有一些我的自定义服务,缓存,日志神马的代码就不贴了)

import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { RoutingService } from './routing.service';
import { LogService } from './log.service';
import { AppStorageService } from './app-storage.service';
import { Bean } from '../bean/bean';
import { Http, Headers } from '@angular/http';

@Injectable()
export class AuthGuardService implements CanActivate {
  headers: Headers;
  canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
    if (this.appStorageService.getLoginFlag()) {
      this.logger.debug('[auth guard] auth success. username:'+this.appStorageService.getUserName());
      return true;
    } else {
      this.logger.debug('[auth guard] auth fail.')
      this.routingService.toLogin();
      return false;
    }
  }

  constructor(
    private http: Http,
    private logger: LogService,
    private routingService: RoutingService,
    private appStorageService: AppStorageService
  ) {  }

  auth(userName: string, password: string): void {
    let that = this;
    this.logger.info('[auth guard] start loging... userName:' + userName + ', password:' + password);
    this.headers = new Headers({ 'Content-Type': 'application/x-www-form-urlencoded' });
    this.headers.append(document.getElementsByName('_csrf_header')[0].getAttribute('content'), document.getElementsByName('_csrf')[0].getAttribute('content'));
    this.http.post(this.appStorageService.getCtx() + '/doLogin', 'userName=' + userName + '&password=' + password, { headers: this.headers }).subscribe(function (res) {
      that.logger.info('[auth guard] login result');
      that.logger.info(res.toString());
    })
  }
}

步骤2:增加退出代码

调用接口退出后端SpringSecurity的登录状态,清除Angular2内SessionStorage的登录信息即可。其中SpringSecurity登出是必要步骤,否则页面上是退出了,但是实际上调用后端api接口仍然是有权限的。

代码如下

  doLogout() {
    let that = this;
    if (!that.appStorageService.getLoginFlag()) {
      that.layerService.toast('您未登录');
    } else {
      this.layerService.confirm('确认退出吗?', {
        btns: ['退出', '取消'],
        onConfirm: function () {
          that.apiService.doLogout().then(function () {
            that.appStorageService.setLoginFlag(false);
            that.appStorageService.setUserName('');
            that.layerService.toast('登出成功');
          }).catch(err => that.layerService.toast(err));
        }
      })
    }
  }

步骤3:改造路由

在routes数组中为需要验证权限的路径添加canActivate接口。

import { NgModule, OnInit } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { AuthGuardService } from '../services/auth-guard.service';

import { PublicComponent } from '../public.component';
import { PrivateComponent } from '../private.component';

const routes: Routes = [
    { path: '/spa-view/public', component: PublicComponent},
    { path: '/spa-view/private', component: PrivateComponent, canActivate: [AuthGuardService] },
]

@NgModule({
    imports: [RouterModule.forRoot(routes)],
    exports: [RouterModule]
})

export class AppRoutingModule {
}

猜你喜欢

转载自blog.csdn.net/tzdwsy/article/details/59005581