一、前端实现

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代码实现的更多相关文章

  1. 基于spring security 实现前后端分离项目权限控制

    前后端分离的项目,前端有菜单(menu),后端有API(backendApi),一个menu对应的页面有N个API接口来支持,本文介绍如何基于spring security实现前后端的同步权限控制. ...

  2. 基于 Spring Security 的前后端分离的权限控制系统

    话不多说,入正题.一个简单的权限控制系统需要考虑的问题如下: 权限如何加载 权限匹配规则 登录 1.  引入maven依赖 1 <?xml version="1.0" enc ...

  3. 喜大普奔,两个开源的 Spring Boot + Vue 前后端分离项目可以在线体验了

    折腾了一周的域名备案昨天终于搞定了. 松哥第一时间想到赶紧把微人事和 V 部落部署上去,我知道很多小伙伴已经等不及了. 1. 也曾经上过线 其实这两个项目当时刚做好的时候,我就把它们部署到服务器上了, ...

  4. 两个开源的 Spring Boot + Vue 前后端分离项目

    折腾了一周的域名备案昨天终于搞定了. 松哥第一时间想到赶紧把微人事和 V 部落部署上去,我知道很多小伙伴已经等不及了. 1. 也曾经上过线 其实这两个项目当时刚做好的时候,我就把它们部署到服务器上了, ...

  5. 实战!spring Boot security+JWT 前后端分离架构认证登录!

    大家好,我是不才陈某~ 认证.授权是实战项目中必不可少的部分,而Spring Security则将作为首选安全组件,因此陈某新开了 <Spring Security 进阶> 这个专栏,写一 ...

  6. Spring Boot + Vue 前后端分离开发,权限管理的一点思路

    在传统的前后端不分的开发中,权限管理主要通过过滤器或者拦截器来进行(权限管理框架本身也是通过过滤器来实现功能),如果用户不具备某一个角色或者某一个权限,则无法访问某一个页面. 但是在前后端分离中,页面 ...

  7. 如何使用Spring Securiry实现前后端分离项目的登录功能

    如果不是前后端分离项目,使用SpringSecurity做登录功能会很省心,只要简单的几项配置,便可以轻松完成登录成功失败的处理,当访问需要认证的页面时,可以自动重定向到登录页面.但是前后端分离的项目 ...

  8. Keycloak快速上手指南,只需10分钟即可接入Spring Boot/Vue前后端分离应用实现SSO单点登录

    登录及身份认证是现代web应用最基本的功能之一,对于企业内部的系统,多个系统往往希望有一套SSO服务对企业用户的登录及身份认证进行统一的管理,提升用户同时使用多个系统的体验,Keycloak正是为此种 ...

  9. Angular企业级开发(9)-前后端分离之后添加验证码

    1.背景介绍 团队开发的项目,前端基于Bootstrap+AngularJS,后端Spring MVC以RESTful接口给前端调用.开发和部署都是前后端分离.项目简单部署图如下,因为后台同时采用微服 ...

  10. 一个实际的案例介绍Spring Boot + Vue 前后端分离

    介绍 最近在工作中做个新项目,后端选用Spring Boot,前端选用Vue技术.众所周知现在开发都是前后端分离,本文就将介绍一种前后端分离方式. 常规的开发方式 采用Spring Boot 开发项目 ...

随机推荐

  1. VMware station 安装报错 failed to install the hcmon driver

    VMware station 安装报错 failed to install the hcmon driver 1.将 C:\Windows\System32\drivers 下的hcmon.sys改名 ...

  2. idea各种中文显示乱码解决大全

    本文链接:https://blog.csdn.net/liqimo1799/article/details/81811153中文乱码问题分类: 编码普通中文乱码properties文件中文乱码cons ...

  3. 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 ...

  4. uni-app 常用框架内置方法 更新中 .....

    获取 登录信息,getStorage 初始化页面数据   请求  下拉刷新页面 加载更多  点击跳转  个人中心  uni.request(OBJECT)   success=成功    fail=失 ...

  5. P1052 国王放置问题

    题目描述 在n*m的棋盘上放置k个国王,要求k个国王互相不攻击,有多少种不同的放置方法.假设国王放置在第(x,y)格,国王的攻击的区域是:(x-1,y-1), (x-1,y),(x-1,y+1),(x ...

  6. MySQL Workbench: mysqldump version mismatch

    Windows10 64bit系统下,步骤就是: Edit --> preferences --> Administrator --> Path to mysqldump tool: ...

  7. navicat for mysql (10038)如何解决

    1.授权(youpassword修改为你的密码) #本机登陆mysql: $:mysql -u root -p #改变数据库: mysql>use mysql; #从所有主机: mysql> ...

  8. ASP.NET MVC4.0+EF+LINQ+bui+网站+角色权限管理系统(7)

    今天将使用Simplemembership进行权限控制 我们使用mvc的AuthorizeAttribute来实现对Controller and Action权限控制 看如下标为红色的代码片段: // ...

  9. Spring Security 学习笔记-信道过滤器

    信道过滤器主要职责是拦截不合规则的http请求,比如规定只能通过https访问资源,那么信道拦截器做相应的拦截处理,把http请求重定向为https请求,https请求则不做任何处理. 配置方式参照: ...

  10. Vue的数据双向绑定和Object.defineProperty()

    Vue是前端三大框架之一,也被很多人指责抄袭,说他的两个核心功能,一个数据双向绑定,一个组件化分别抄袭angular的数据双向绑定和react的组件化思想,咱们今天就不谈这种大是大非,当然我也没到达那 ...