(二)Angular+spring-security-cas前后端分离(基于ticket代码实现
一、前端实现
1.1、路由守卫(用于拦截路由认证)
import { Injectable, Inject } from "@angular/core";
import {
CanActivate,
ActivatedRouteSnapshot,
RouterStateSnapshot,
Router,
NavigationStart
} from "@angular/router";
import { SessionStorageServiceService } from "./session-storage-service.service";
import { AuthenticateServiceService } from "./authenticate-service.service";
@Injectable()
export class AuthCanActivate implements CanActivate {
// cas认证地址
private casAuthenticateURL = "http://192.1.0.126:8080/dcas-web"; constructor(
private router: Router,
private sessionStorageServiceService: SessionStorageServiceService,
private authenticateServiceService: AuthenticateServiceService
) {}
// 路由守卫
canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): boolean {
let paramTicket = this.getQueryString("ticket");
if (paramTicket) {
//cas认证后跳回
let ticketValidator = this.authenticateServiceService.ticketValidator(
paramTicket,
window.location.origin + window.location.pathname
);
if (ticketValidator) {
this.sessionStorageServiceService.setTicketToSessionStorage(
paramTicket
);
//把ticket去掉操作
window.location.href = route.routeConfig.path;
}
} else {
let ticket: String = this.sessionStorageServiceService.getTicketToSessionStorage();
if (!ticket) {
ticket = this.authenticateServiceService.getTicket();
if (ticket) {
this.sessionStorageServiceService.setTicketToSessionStorage(ticket);
}
}
if (ticket) {
return true;
}
//需要跳转认证--用angular Api
window.location.href =
this.casAuthenticateURL + "/login?service=" + window.location.href;
}
return false;
}
getQueryString(name: String) {
var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i");
var r = window.location.search.substr(1).match(reg);
if (r != null) {
//中文转码
return decodeURI(r[2]);
}
return null;
}
}
1.2、拦截器(用于拦截ajax请求)
import { Injectable } from "@angular/core";
import {
HttpEvent,
HttpInterceptor,
HttpHandler,
HttpRequest,
HttpResponse,
HttpHeaderResponse
} from "@angular/common/http";
import { catchError, mergeMap } from "rxjs/operators";
import { ErrorObservable } from "rxjs/observable/ErrorObservable";
import { SessionStorageServiceService } from "./session-storage-service.service";
import { Observable } from "rxjs/Observable"; @Injectable()
export class AutuHttpclientInterceptor implements HttpInterceptor {
constructor(
private sessionStorageServiceService: SessionStorageServiceService
) {}
intercept(
req: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
//对任意请求的url,header添加ticket参数
//判断ticket--TODO
const authReq = req.clone({
//url: (req.url + '&ticket='+this.sessionStorageServiceService.getTicketToSessionStorage())
headers: req.headers
.set(
"ticket",
this.sessionStorageServiceService.getTicketToSessionStorage() as string
)
//标识异步请求
.set("X-Requested-With", "XMLHttpRequest")
});
return next.handle(authReq).pipe(
mergeMap((event: any) => {
if (event instanceof HttpResponse) {
//ticket无效,引导用户去认证
if (event.headers.get("ticket") == "INVALID_TICKET") {
this.sessionStorageServiceService.deleteTicketToSessionStorage();
alert("ticket无效,引导用户去认证");
} else if (event instanceof HttpResponse && event.status != 200) {
return ErrorObservable.create(event);
} else if (event.headers.get("ticket")) {
//前端更新ticket
this.sessionStorageServiceService.setTicketToSessionStorage(
event.headers.get("ticket")
);
}
}
//请求成功返回响应
return Observable.create(observer => {
observer.next(event);
});
}),
catchError((res: HttpResponse<any>) => {
//请求失败处理
alert(res.status + "错误,请联系管理员");
console.error(res.status + "错误,请联系管理员");
return ErrorObservable.create(event);
}) as any
);
}
}
1.3、session存取服务
import { Injectable } from "@angular/core"; @Injectable()
export class SessionStorageServiceService {
constructor() {} private RMK_TICKET = "rmk_ticket";
/**
* 设置缓存
* @param key
* @param obj
*/
public setTicketToSessionStorage(ticket: String): void {
sessionStorage.setItem(this.RMK_TICKET, ticket as string);
}
/**
*
* @param key 获取缓存
*/
public getTicketToSessionStorage(): String {
return sessionStorage.getItem(this.RMK_TICKET) as String;
}
/**
* 删除ticket
*/
public deleteTicketToSessionStorage(): void {
sessionStorage.removeItem(this.RMK_TICKET);
}
}
1.4、获取、认证ticket前端服务
import { Injectable } from "@angular/core";
import { ajax } from "rxjs/ajax";
@Injectable()
export class AuthenticateServiceService {
constructor() {} // 获取ticket
private getTicketUrl = "/apps-web/rest/authenticate/getTicket?1=1";
//认证ticket是否有效
private ticketValidatorUrl = "/apps-web/authenticate/ticketValidator?1=1"; /**
* 同步请求获取ticket
*/
getTicket(): String {
let ticket: String = null;
ajax({
url: this.getTicketUrl,
method: "GET",
async: false,
responseType: "json"
}).subscribe(
res => {
ticket = res.response as String;
},
error => {
console.error(error);
}
);
return ticket;
} ticketValidator(ticket: String, service: String): Boolean {
let ticketValidate: Boolean = false;
ajax({
url:
this.ticketValidatorUrl + "&ticket=" + ticket + "&service=" + service,
method: "GET",
async: false,
responseType: "json"
}).subscribe(
res => {
ticketValidate = res.response as Boolean;
},
error => {
console.error(error);
}
);
return ticketValidate;
}
}
1.5、app.module.ts配置
import { BrowserModule } from "@angular/platform-browser";
import { NgModule } from "@angular/core";
import { RouterModule, Routes } from "@angular/router";
import { HttpClientModule, HTTP_INTERCEPTORS } from "@angular/common/http";
import { AppComponent } from "./app.component";
import { HomeComponent } from "./home/home.component";
import { HttpModule } from "@angular/http";
import { AuthCanActivate } from "./auth.can.activate";
import { AutuHttpclientInterceptor } from "./autu-httpclient-interceptor";
import { AuthenticateServiceService } from "./authenticate-service.service";
import { SessionStorageServiceService } from "./session-storage-service.service";
import { WebSocketServiceService } from "./web-socket-service.service";
import { UserInfoServiceService } from "./user-info-service.service"; export const routes: Routes = [
{
path: "home",
component: HomeComponent,
canActivate: [AuthCanActivate]
}
]; @NgModule({
declarations: [AppComponent, HomeComponent],
imports: [
BrowserModule,
HttpModule,
HttpClientModule,
RouterModule.forRoot(routes)
],
providers: [
AuthCanActivate,
AuthenticateServiceService,
SessionStorageServiceService,
WebSocketServiceService,
UserInfoServiceService,
{
provide: HTTP_INTERCEPTORS,
useClass: AutuHttpclientInterceptor,
multi: true
}
],
bootstrap: [AppComponent]
})
export class AppModule {}
二、后端代码实现
2.1 TicketCodeAuthenticationFilter实现
import java.io.IOException; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import org.springframework.security.cas.ServiceProperties;
import org.springframework.security.cas.web.CasAuthenticationFilter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import *.security.authentication.TicketCodeAuthenticationToken;
import *.security.web.authentication.TicketServiceAuthenticationDetails; /**
*
* ticket认证拦截器
*
*
*/
public class TicketCodeAuthenticationFilter extends CasAuthenticationFilter { // =================================================================================================== public TicketCodeAuthenticationFilter() {
// 指定当前过滤器处理的请求
// super("/authentication/ticketValidator", "GET");
super.setRequiresAuthenticationRequestMatcher(
new AntPathRequestMatcher("/authenticate/ticketValidator", "GET"));
} @Override
public Authentication attemptAuthentication(final HttpServletRequest request, final HttpServletResponse response)
throws AuthenticationException, IOException {
final String username = CAS_STATELESS_IDENTIFIER;
// 获取ticket
String password = obtainArtifact(request);
String service = obtainService(request);
if (password == null) {
logger.debug("获取认证票据失败!");
password = "";
}
final TicketCodeAuthenticationToken authRequest = new TicketCodeAuthenticationToken(username, password,
service);
authRequest.setDetails(this.buildDetails(service));
return this.getAuthenticationManager().authenticate(authRequest);
} /**
*
* 构造TicketServiceAuthenticationDetails
*
* @param service
* @return
*/
public TicketServiceAuthenticationDetails buildDetails(String service) {
return new TicketServiceAuthenticationDetails(service);
} /**
*
* 获取认证地址
*
* @param request
* @return
*/
protected String obtainService(HttpServletRequest request) {
return request.getParameter(ServiceProperties.DEFAULT_CAS_SERVICE_PARAMETER);
} }
2.2 TicketCodeAuthenticationToken
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; /**
* 封装ticket登陆Token类 */
public class TicketCodeAuthenticationToken extends UsernamePasswordAuthenticationToken { private static final long serialVersionUID = 1L; public TicketCodeAuthenticationToken(Object principal, Object credentials, String authenticationUrl) {
super(principal, credentials);
this.principal = principal;
this.credentials = credentials;
this.authenticationUrl = authenticationUrl;
} private final Object principal; private Object credentials; // 认证地址
private String authenticationUrl; public Object getCredentials() {
return this.credentials;
} public void setCredentials(Object credentials) {
this.credentials = credentials;
} public Object getPrincipal() {
return this.principal;
} public String getAuthenticationUrl() {
return this.authenticationUrl;
} }
2.3 TicketServiceAuthenticationDetails
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.cas.web.authentication.ServiceAuthenticationDetails; public class TicketServiceAuthenticationDetails implements ServiceAuthenticationDetails { /** 成员变量:TODO 在这里请添加变量serialVersionUID的描述 */
private static final long serialVersionUID = 1L;
/** 静态变量:系统日志 */
private static final Log logger = LogFactory.getLog(TicketServiceAuthenticationDetails.class); private String serviceUrl; /*
* (non-Javadoc)
*
* @see org.springframework.security.cas.web.authentication.
* ServiceAuthenticationDetails#getServiceUrl()
*/
@Override
public String getServiceUrl() {
return serviceUrl;
} public TicketServiceAuthenticationDetails(String serviceUrl) {
super();
this.serviceUrl = serviceUrl;
}
}
2.4 TicketAuthenticationAjaxRequestFilter
import java.io.IOException; import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.jasig.cas.client.util.AbstractCasFilter;import org.springframework.cache.Cache;
import org.springframework.cache.Cache.ValueWrapper;
import org.springframework.cache.CacheManager;
import org.springframework.security.cas.ServiceProperties;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext; import net.sf.ehcache.Ehcache;
import net.sf.ehcache.Element; /**
*
* 前后端分离基于ticket的ajax拦截器
* */
public class TicketAuthenticationAjaxRequestFilter implements Filter {
// 无效票据标识
public static final String INVALID_TICKET = "INVALID_TICKET"; private String excludePaths; private Ehcache cache;
/**
* 要排除的url路径
*/
private String[] excludePathArrays; /*
* (non-Javadoc)
*
* @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
*/
@Override
public void init(FilterConfig filterConfig) throws ServletException {
if (!StringUtil.isBlank(excludePaths)) {
excludePathArrays = excludePaths.trim().split(",");
} else {
excludePathArrays = new String[10];
}
} @Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
throws IOException, ServletException {
if (excludePathArrays == null) {
this.init(null);
}
final HttpServletRequest httpServletRequest = (HttpServletRequest) request;
final HttpServletResponse httpServletResponse = (HttpServletResponse) response;
String uri = httpServletRequest.getRequestURI();
String requestType = httpServletRequest.getHeader("X-Requested-With");
// 非ajax请求,直接放行
if (StringUtil.isBlank(requestType)) {
filterChain.doFilter(request, response);
return;
}
// ajax请求不需要拦截的直接放行
if (excludePathArrays != null && excludePathArrays.length > 0 && !StringUtil.isBlank(uri)) {
for (String path : excludePathArrays) {
if (!StringUtil.isBlank(path)) {
if (uri.contains(path)) {
filterChain.doFilter(request, response);
return;
}
}
}
}
String ticket = httpServletRequest.getHeader(ServiceProperties.DEFAULT_CAS_ARTIFACT_PARAMETER);
Authentication sharedAuthentication = null;
if (!StringUtil.isBlank(ticket) && cache != null) {
Element element = cache.get(ticket);
// 取出共享缓存中的认证信息,认证的时候cacheData写入
if (element != null && element.getValue() instanceof Authentication) {
sharedAuthentication = (Authentication) element.getValue();
}
}
SecurityContext securityContext =SecurityContextHolder.getContext();
Authentication sessionAssertion = securityContext.getAuthentication();
if (sessionAssertion == null && sharedAuthentication != null) {
// session中不存在认证信息,且sharedAssertion 不为空,将sharedAssertion
securityContext.setAuthentication(sharedAuthentication);
filterChain.doFilter(request, response);
return;
}
String sessionAssertionTicket = sessionAssertion == null ? null : sessionAssertion.getCredentials().toString();
String sharedAssertionTicket = sharedAuthentication == null ? null
: sharedAuthentication.getCredentials().toString();
/**
* 两个ticket不一致时,设置header,前端跟新ticket TODO
*/
if (StringUtil.isBlank(sessionAssertionTicket) || !sessionAssertionTicket.equals(sharedAssertionTicket)) {
// sharedAssertion不为空,且sharedAssertion与session中的用户不一致
securityContext.setAuthentication(sharedAuthentication);
httpServletResponse.setHeader(ServiceProperties.DEFAULT_CAS_ARTIFACT_PARAMETER, sharedAssertionTicket);
}
if (securityContext.getAuthentication().isAuthenticated()) {
// session中存在认证信息也放行
httpServletResponse.setHeader(ServiceProperties.DEFAULT_CAS_ARTIFACT_PARAMETER, sessionAssertionTicket);
filterChain.doFilter(request, response);
return;
}
// 返回无效票据标识
httpServletResponse.setHeader(ServiceProperties.DEFAULT_CAS_ARTIFACT_PARAMETER, INVALID_TICKET);
return;
} @Override
public void destroy() { } public String[] getExcludePathArrays() {
return excludePathArrays;
} public void setExcludePathArrays(String[] excludePathArrays) {
this.excludePathArrays = excludePathArrays;
} public String getExcludePaths() {
return excludePaths;
} public void setExcludePaths(String excludePaths) {
this.excludePaths = excludePaths;
} public Ehcache getCache() {
return this.cache;
} public void setCache(Ehcache cache) {
this.cache = cache;
}
}
2.5 ReturnAuthenticationSuccessHandler
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jasig.cas.client.util.AbstractCasFilter;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.WebAttributes;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
import org.springframework.security.web.savedrequest.RequestCache;
import org.springframework.security.web.savedrequest.SavedRequest; import *.client.security.TicketCodeAuthenticationFilter; /**
*
* ticket认证成功处理器 */
public class ReturnAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
protected final Log logger = LogFactory.getLog(this.getClass()); private CacheManager cacheManager; @Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws ServletException, IOException {
clearAuthenticationAttributes(request);
response.getWriter().write("true");
} /**
* Removes temporary authentication-related data which may have been stored
* in the session during the authentication process.
*/
protected final void clearAuthenticationAttributes(HttpServletRequest request) {
HttpSession session = request.getSession(false); if (session == null) {
return;
} session.removeAttribute(WebAttributes.AUTHENTICATION_EXCEPTION);
} // public void setRequestCache(RequestCache requestCache) {
// this.requestCache = requestCache;
// } public CacheManager getCacheManager() {
return this.cacheManager;
} public void setCacheManager(CacheManager cacheManager) {
this.cacheManager = cacheManager;
} // public RequestCache getRequestCache() {
// return this.requestCache;
// } }
注意:注意过滤、拦截器在spring认证链的中顺序。
(二)Angular+spring-security-cas前后端分离(基于ticket代码实现的更多相关文章
- 基于spring security 实现前后端分离项目权限控制
前后端分离的项目,前端有菜单(menu),后端有API(backendApi),一个menu对应的页面有N个API接口来支持,本文介绍如何基于spring security实现前后端的同步权限控制. ...
- 基于 Spring Security 的前后端分离的权限控制系统
话不多说,入正题.一个简单的权限控制系统需要考虑的问题如下: 权限如何加载 权限匹配规则 登录 1. 引入maven依赖 1 <?xml version="1.0" enc ...
- 喜大普奔,两个开源的 Spring Boot + Vue 前后端分离项目可以在线体验了
折腾了一周的域名备案昨天终于搞定了. 松哥第一时间想到赶紧把微人事和 V 部落部署上去,我知道很多小伙伴已经等不及了. 1. 也曾经上过线 其实这两个项目当时刚做好的时候,我就把它们部署到服务器上了, ...
- 两个开源的 Spring Boot + Vue 前后端分离项目
折腾了一周的域名备案昨天终于搞定了. 松哥第一时间想到赶紧把微人事和 V 部落部署上去,我知道很多小伙伴已经等不及了. 1. 也曾经上过线 其实这两个项目当时刚做好的时候,我就把它们部署到服务器上了, ...
- 实战!spring Boot security+JWT 前后端分离架构认证登录!
大家好,我是不才陈某~ 认证.授权是实战项目中必不可少的部分,而Spring Security则将作为首选安全组件,因此陈某新开了 <Spring Security 进阶> 这个专栏,写一 ...
- Spring Boot + Vue 前后端分离开发,权限管理的一点思路
在传统的前后端不分的开发中,权限管理主要通过过滤器或者拦截器来进行(权限管理框架本身也是通过过滤器来实现功能),如果用户不具备某一个角色或者某一个权限,则无法访问某一个页面. 但是在前后端分离中,页面 ...
- 如何使用Spring Securiry实现前后端分离项目的登录功能
如果不是前后端分离项目,使用SpringSecurity做登录功能会很省心,只要简单的几项配置,便可以轻松完成登录成功失败的处理,当访问需要认证的页面时,可以自动重定向到登录页面.但是前后端分离的项目 ...
- Keycloak快速上手指南,只需10分钟即可接入Spring Boot/Vue前后端分离应用实现SSO单点登录
登录及身份认证是现代web应用最基本的功能之一,对于企业内部的系统,多个系统往往希望有一套SSO服务对企业用户的登录及身份认证进行统一的管理,提升用户同时使用多个系统的体验,Keycloak正是为此种 ...
- Angular企业级开发(9)-前后端分离之后添加验证码
1.背景介绍 团队开发的项目,前端基于Bootstrap+AngularJS,后端Spring MVC以RESTful接口给前端调用.开发和部署都是前后端分离.项目简单部署图如下,因为后台同时采用微服 ...
- 一个实际的案例介绍Spring Boot + Vue 前后端分离
介绍 最近在工作中做个新项目,后端选用Spring Boot,前端选用Vue技术.众所周知现在开发都是前后端分离,本文就将介绍一种前后端分离方式. 常规的开发方式 采用Spring Boot 开发项目 ...
随机推荐
- VMware station 安装报错 failed to install the hcmon driver
VMware station 安装报错 failed to install the hcmon driver 1.将 C:\Windows\System32\drivers 下的hcmon.sys改名 ...
- idea各种中文显示乱码解决大全
本文链接:https://blog.csdn.net/liqimo1799/article/details/81811153中文乱码问题分类: 编码普通中文乱码properties文件中文乱码cons ...
- java.lang.ClassCastException: com.sun.proxy.$Proxy6 cannot be cast to com.etc.service.serviceImpl.BankServiceImpl
错误原因: java.lang.ClassCastException: com.sun.proxy.$Proxy6 cannot be cast to com.etc.service.serviceI ...
- uni-app 常用框架内置方法 更新中 .....
获取 登录信息,getStorage 初始化页面数据 请求 下拉刷新页面 加载更多 点击跳转 个人中心 uni.request(OBJECT) success=成功 fail=失 ...
- P1052 国王放置问题
题目描述 在n*m的棋盘上放置k个国王,要求k个国王互相不攻击,有多少种不同的放置方法.假设国王放置在第(x,y)格,国王的攻击的区域是:(x-1,y-1), (x-1,y),(x-1,y+1),(x ...
- MySQL Workbench: mysqldump version mismatch
Windows10 64bit系统下,步骤就是: Edit --> preferences --> Administrator --> Path to mysqldump tool: ...
- navicat for mysql (10038)如何解决
1.授权(youpassword修改为你的密码) #本机登陆mysql: $:mysql -u root -p #改变数据库: mysql>use mysql; #从所有主机: mysql> ...
- ASP.NET MVC4.0+EF+LINQ+bui+网站+角色权限管理系统(7)
今天将使用Simplemembership进行权限控制 我们使用mvc的AuthorizeAttribute来实现对Controller and Action权限控制 看如下标为红色的代码片段: // ...
- Spring Security 学习笔记-信道过滤器
信道过滤器主要职责是拦截不合规则的http请求,比如规定只能通过https访问资源,那么信道拦截器做相应的拦截处理,把http请求重定向为https请求,https请求则不做任何处理. 配置方式参照: ...
- Vue的数据双向绑定和Object.defineProperty()
Vue是前端三大框架之一,也被很多人指责抄袭,说他的两个核心功能,一个数据双向绑定,一个组件化分别抄袭angular的数据双向绑定和react的组件化思想,咱们今天就不谈这种大是大非,当然我也没到达那 ...