首页 > 编程语言 > 详细

(二)Angular+spring-security-cas前后端分离(基于ticket代码实现

时间:2020-01-07 19:56:45      阅读:84      评论:0      收藏:0      [点我收藏+]

一、前端实现

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代码实现

原文:https://www.cnblogs.com/lijjingchuan/p/12162868.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!