在配置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 {
}