前言:今天在網上無意間看到cas單點登錄排除請求的問題,發現很多人在討論如何通過改寫AuthenticationFilter類來實現忽略/排除請求URL的功能;突發奇想搜了一下,還真蠻多人都是這么干的,原諒我是個耿直的boy,當時我笑的飯都噴出來了,只需要一個配置的問題,被你們搞的這么麻煩;雖然很想回復他們“你們這幫人用別人的東西都不看源碼的嗎?”,轉念一想,這也要怪作者不給力,文檔里壓根沒有提到這個配置,在這里用少量篇幅講解如何配置排除不需要攔截的請求URL,后面用大量篇幅介紹我是如何從源碼中得知這個配置的,希望對大家有用!做好自己!--eguid始終堅持原創的開源技術文章分享,博客園與本博客保持同步更新。歡迎大家加群一起交流:608423839
1、cas-client單點登錄配置
http://blog.csdn.net/eguid_1/article/details/51278622,cas-client完整配置。
沒有實現忽略/排除請求URL的cas-client登錄驗證過濾器
- <filter>
- <filter-name>casAuthenticationFilter</filter-name>
- <filter-class>org.jasig.cas.client.authentication.AuthenticationFilter</filter-class>
- <init-param>
- <param-name>casServerLoginUrl</param-name>
- <param-value>https://cas.eguid.cc/cas-server/</param-value>
- </init-param>
- <init-param>
- <param-name>serverName</param-name>
- <param-value>http://client.eguid.cc/</param-value>
- </init-param>
- </filter>
- <filter-mapping>
- <filter-name>casAuthenticationFilter</filter-name>
- <url-pattern>/*</url-pattern>
- </filter-mapping>
這個配置依然是可用的,當然我們要實現忽略/排除請求URL的功能,那么我們該怎么做呢?
2、忽略/排除多個請求URL
- <filter>
- <filter-name>casAuthenticationFilter</filter-name>
- <filter-class>org.jasig.cas.client.authentication.AuthenticationFilter</filter-class>
- <init-param>
- <param-name>casServerLoginUrl</param-name>
- <param-value>http://cas.eguid.cc/cas-server/</param-value>
- </init-param>
- <init-param>
- <param-name>serverName</param-name>
- <param-value>http://cilent.eguid.cc/</param-value>
- <param-name>ignorePattern</param-name>
- <param-value>/js/*|/img/*|/view/*|/css/*</param-value>
- </init-param>
- </filter><!--做好自己!eguid原創-->
- <filter-mapping>
- <filter-name>casAuthenticationFilter</filter-name>
- <url-pattern>/*</url-pattern>
- </filter-mapping>
如上所見,我們排除了四個請求URL(必須是正則表達式形式,下面會講為什么要這么配置)
3、cas-client默認登錄驗證過濾器源碼解析
看源碼,一定要帶着目的去看;我們的目的就是找AuthenticationFilter這個cas-client默認登錄驗證過濾器是否具有排除登錄請求URL的功能。
(1)打開cas-client項目源碼
打開github上的cas-client項目,可以把項目導到本地或者直接在github上找到org.jasig.cas.client.authentication.AuthenticationFilter.Java這個類。
(2)登錄驗證過濾器AuthenticationFilter的doFilter
既然是個過濾器,就直接找到該類的doFilter方法
- <span style="color:#24292e;"> public final void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse,
- final FilterChain filterChain) throws IOException, ServletException {
- <!--做好自己!eguid原創-->
- final HttpServletRequest request = (HttpServletRequest) servletRequest;
- final HttpServletResponse response = (HttpServletResponse) servletResponse;
- if (</span><span style="color:#ff0000;">isRequestUrlExcluded</span><span style="color:#24292e;">(request)) {
- logger.debug("Request is ignored.");
- filterChain.doFilter(request, response);
- return;
- }
- final HttpSession session = request.getSession(false);
- final Assertion assertion = session != null ? (Assertion) session.getAttribute(CONST_CAS_ASSERTION) : null;
- if (assertion != null) {
- filterChain.doFilter(request, response);
- return;
- }
- final String serviceUrl = constructServiceUrl(request, response);
- final String ticket = retrieveTicketFromRequest(request);
- final boolean wasGatewayed = this.gateway && this.gatewayStorage.hasGatewayedAlready(request, serviceUrl);
- if (CommonUtils.isNotBlank(ticket) || wasGatewayed) {
- filterChain.doFilter(request, response);
- return;
- }
- final String modifiedServiceUrl;
- logger.debug("no ticket and no assertion found");
- if (this.gateway) {
- logger.debug("setting gateway attribute in session");
- modifiedServiceUrl = this.gatewayStorage.storeGatewayInformation(request, serviceUrl);
- } else {
- modifiedServiceUrl = serviceUrl;
- }
- logger.debug("Constructed service url: {}", modifiedServiceUrl);
- final String urlToRedirectTo = CommonUtils.constructRedirectUrl(this.casServerLoginUrl,
- getProtocol().getServiceParameterName(), modifiedServiceUrl, this.renew, this.gateway);
- logger.debug("redirecting to \"{}\"", urlToRedirectTo);
- this.authenticationRedirectStrategy.redirect(request, response, urlToRedirectTo);
- }</span>
(3)isRequestUrlExcluded方法
第一眼就看到了上面代碼紅色標識處的isRequestUrlExcluded,這個意思很直白,判斷是不是需要忽略/排除的請求URL。
繼續接着找到isRequestUrlExcluded這個方法的實現代碼:
- <span style="color:#24292e;"> private boolean isRequestUrlExcluded(final HttpServletRequest request) {
- if (this.ignoreUrlPatternMatcherStrategyClass == null) {
- return false;
- }
- <!--做好自己!eguid原創-->
- final StringBuffer urlBuffer = request.getRequestURL();
- if (request.getQueryString() != null) {
- urlBuffer.append("?").append(request.getQueryString());
- }
- final String requestUri = urlBuffer.toString();
- return this.</span><span style="color:#ff0000;">ignoreUrlPatternMatcherStrategyClass</span><span style="color:#24292e;">.matches(requestUri);
- }</span>
看紅色標識位置的名字,這里用到了UrlPatternMatcherStrategy這個類,意思很簡單直白:‘請求url的匹配策略類’,暫時還不知道這里是正則匹配,往后看:
(4)請求URL的匹配策略類UrlPatternMatcherStrategy
- private UrlPatternMatcherStrategy ignoreUrlPatternMatcherStrategyClass = null;
發現該類是在初始化方法中進行初始化的:
- <span style="color:#24292e;"> protected void initInternal(final FilterConfig filterConfig) throws ServletException {
- if (!isIgnoreInitConfiguration()) {
- super.initInternal(filterConfig);
- setCasServerLoginUrl(getString(ConfigurationKeys.CAS_SERVER_LOGIN_URL));
- setRenew(getBoolean(ConfigurationKeys.RENEW));
- setGateway(getBoolean(ConfigurationKeys.GATEWAY));
- <!--做好自己!eguid原創-->
- final String ignorePattern = getString(ConfigurationKeys.</span><span style="color:#ff0000;">IGNORE_PATTERN</span><span style="color:#24292e;">);
- final String ignoreUrlPatternType = getString(ConfigurationKeys.</span><span style="color:#ff0000;">IGNORE_URL_PATTERN_TYPE</span><span style="color:#24292e;">);
- if (ignorePattern != null) {
- final Class<? extends UrlPatternMatcherStrategy> ignoreUrlMatcherClass = PATTERN_MATCHER_TYPES.get(ignoreUrlPatternType);
- if (ignoreUrlMatcherClass != null) {
- this.ignoreUrlPatternMatcherStrategyClass = ReflectUtils.newInstance(ignoreUrlMatcherClass.getName());
- } else {
- try {
- logger.trace("Assuming {} is a qualified class name...", ignoreUrlPatternType);
- this.ignoreUrlPatternMatcherStrategyClass = ReflectUtils.newInstance(ignoreUrlPatternType);
- } catch (final IllegalArgumentException e) {
- logger.error("Could not instantiate class [{}]", ignoreUrlPatternType, e);
- }
- }
- if (this.ignoreUrlPatternMatcherStrategyClass != null) {
- this.ignoreUrlPatternMatcherStrategyClass.setPattern(ignorePattern);
- }
- }
- final Class<? extends GatewayResolver> gatewayStorageClass = getClass(ConfigurationKeys.GATEWAY_STORAGE_CLASS);
- if (gatewayStorageClass != null) {
- setGatewayStorage(ReflectUtils.newInstance(gatewayStorageClass));
- }
- final Class<? extends AuthenticationRedirectStrategy> authenticationRedirectStrategyClass = getClass(ConfigurationKeys.AUTHENTICATION_REDIRECT_STRATEGY_CLASS);
- if (authenticationRedirectStrategyClass != null) {
- this.authenticationRedirectStrategy = ReflectUtils.newInstance(authenticationRedirectStrategyClass);
- }
- }
- }</span>
雖然使用了反射,但是依然不影響我們找到根本所在,找到ConfigurationKeys這個類里面的變量究竟是什么:
- <span style="color:#24292e;"> ConfigurationKey<String> IGNORE_PATTERN = new ConfigurationKey<String>("</span><span style="color:#ff0000;">ignorePattern</span><span style="color:#24292e;">", null);
- ConfigurationKey<String> IGNORE_URL_PATTERN_TYPE = new ConfigurationKey<String>("</span><span style="color:#ff0000;">ignoreUrlPatternType</span><span style="color:#24292e;">", "REGEX");</span>
字面上理解這兩個常量定義了忽略模式以及忽略模式類型是‘正則’,當然我們還是不確定是不是正則,那么繼續往下找
- final Class<? extends UrlPatternMatcherStrategy> ignoreUrlMatcherClass = PATTERN_MATCHER_TYPES.get(ignoreUrlPatternType);
我們已經通過ConfigurationKeys類知道ignoreUrlPatternType是個‘REGEX’字符串,那么
- PATTERN_MATCHER_TYPES.put("REGEX", RegexUrlPatternMatcherStrategy.class);
那么按照REGEX對應的值找到RegexUrlPatternMatcherStrategy這個類:
(5)確定RegexUrlPatternMatcherStrategy類用於處理正則驗證匹配
- public final class RegexUrlPatternMatcherStrategy implements UrlPatternMatcherStrategy {
- <!--做好自己!eguid原創-->
- private Pattern pattern;
- public RegexUrlPatternMatcherStrategy() {}
- public RegexUrlPatternMatcherStrategy(final String pattern) {
- this.setPattern(pattern);
- }
- public boolean matches(final String url) {
- return this.pattern.matcher(url).find();
- }
- public void setPattern(final String pattern) {
- this.pattern = Pattern.compile(pattern);
- }
- }
該類中用到了Pattern來編譯和匹配正則表達式
到這里我們終於可以確定可以用ignorePattern來忽略/排除我們不需要攔截的請求URL,當然必須滿足正則表達式。