前言:今天在网上无意间看到cas单点登录排除请求的问题,发现很多人在讨论如何通过改写AuthenticationFilter类来实现忽略/排除请求URL的功能;突发奇想搜了一下,还真蛮多人都是这么干的,原谅我是个耿直的boy,当时我笑的饭都喷出来了,只需要一个配置的问题,被你们搞的这么麻烦;虽然很想回复他们“你们这帮人用别人的东西都不看源码的吗?”,转念一想,这也要怪作者不给力,文档里压根没有提到这个配置,在这里用少量篇幅讲解如何配置排除不需要拦截的请求URL,后面用大量篇幅介绍我是如何从源码中得知这个配置的,希望对大家有用!做好自己!--eguid始终坚持原创的开源技术文章分享,博客园与本博客保持同步更新。欢迎大家加群一起交流:608423839
1、cas-client单点登录配置
cas-client完整配置。
没有实现忽略/排除请求URL的cas-client登录验证过滤器
1. <filter>
2. <filter-name>casAuthenticationFilter</filter-name>
3. <filter-class>org.jasig.cas.client.authentication.AuthenticationFilter</filter-class>
4. <init-param>
5. <param-name>casServerLoginUrl</param-name>
6. <param-value>https://cas.eguid.cc/cas-server/</param-value>
7. </init-param>
8. <init-param>
9. <param-name>serverName</param-name>
10. <param-value>http://client.eguid.cc/</param-value>
11. </init-param>
12. </filter>
13. <filter-mapping>
14. <filter-name>casAuthenticationFilter</filter-name>
15. <url-pattern>/*</url-pattern>
16. </filter-mapping>
这个配置依然是可用的,当然我们要实现忽略/排除请求URL的功能,那么我们该怎么做呢?
2、忽略/排除多个请求URL
1. <filter>
2. <filter-name>casAuthenticationFilter</filter-name>
3. <filter-class>org.jasig.cas.client.authentication.AuthenticationFilter</filter-class>
4. <init-param>
5. <param-name>casServerLoginUrl</param-name>
6. <param-value>http://cas.eguid.cc/cas-server/</param-value>
7. </init-param>
8. <init-param>
9. <param-name>serverName</param-name>
10. <param-value>http://cilent.eguid.cc/</param-value>
11. <param-name>ignorePattern</param-name>
12. <param-value>/js/*|/img/*|/view/*|/css/*</param-value>
13. </init-param>
14. </filter><!--做好自己!eguid原创-->
15. <filter-mapping>
16. <filter-name>casAuthenticationFilter</filter-name>
17. <url-pattern>/*</url-pattern>
18. </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方法
1. <span style="color:#24292e;"> public final void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse,
2. final FilterChain filterChain) throws IOException, ServletException {
3. <!--做好自己!eguid原创-->
4. final HttpServletRequest request = (HttpServletRequest) servletRequest;
5. final HttpServletResponse response = (HttpServletResponse) servletResponse;
6.
7. if (</span><span style="color:#ff0000;">isRequestUrlExcluded</span><span style="color:#24292e;">(request)) {
8. "Request is ignored.");
9. filterChain.doFilter(request, response);
10. return;
11. }
12.
13. final HttpSession session = request.getSession(false);
14. final Assertion assertion = session != null ? (Assertion) session.getAttribute(CONST_CAS_ASSERTION) : null;
15.
16. if (assertion != null) {
17. filterChain.doFilter(request, response);
18. return;
19. }
20.
21. final String serviceUrl = constructServiceUrl(request, response);
22. final String ticket = retrieveTicketFromRequest(request);
23. final boolean wasGatewayed = this.gateway && this.gatewayStorage.hasGatewayedAlready(request, serviceUrl);
24.
25. if (CommonUtils.isNotBlank(ticket) || wasGatewayed) {
26. filterChain.doFilter(request, response);
27. return;
28. }
29.
30. final String modifiedServiceUrl;
31.
32. "no ticket and no assertion found");
33. if (this.gateway) {
34. "setting gateway attribute in session");
35. this.gatewayStorage.storeGatewayInformation(request, serviceUrl);
36. else {
37. modifiedServiceUrl = serviceUrl;
38. }
39.
40. "Constructed service url: {}", modifiedServiceUrl);
41.
42. final String urlToRedirectTo = CommonUtils.constructRedirectUrl(this.casServerLoginUrl,
43. this.renew, this.gateway);
44.
45. "redirecting to \"{}\"", urlToRedirectTo);
46. this.authenticationRedirectStrategy.redirect(request, response, urlToRedirectTo);
47. }</span>
(3)isRequestUrlExcluded方法
第一眼就看到了上面代码红色标识处的isRequestUrlExcluded,这个意思很直白,判断是不是需要忽略/排除的请求URL。
继续接着找到isRequestUrlExcluded这个方法的实现代码:
1. <span style="color:#24292e;"> private boolean isRequestUrlExcluded(final HttpServletRequest request) {
2. if (this.ignoreUrlPatternMatcherStrategyClass == null) {
3. return false;
4. }
5. <!--做好自己!eguid原创-->
6. final StringBuffer urlBuffer = request.getRequestURL();
7. if (request.getQueryString() != null) {
8. "?").append(request.getQueryString());
9. }
10. final String requestUri = urlBuffer.toString();
11. return this.</span><span style="color:#ff0000;">ignoreUrlPatternMatcherStrategyClass</span><span style="color:#24292e;">.matches(requestUri);
12. }</span>
看红色标识位置的名字,这里用到了UrlPatternMatcherStrategy这个类,意思很简单直白:‘请求url的匹配策略类’,暂时还不知道这里是正则匹配,往后看:
(4)请求URL的匹配策略类UrlPatternMatcherStrategy
- private UrlPatternMatcherStrategy ignoreUrlPatternMatcherStrategyClass = null;
发现该类是在初始化方法中进行初始化的:
1. <span style="color:#24292e;"> protected void initInternal(final FilterConfig filterConfig) throws ServletException {
2. if (!isIgnoreInitConfiguration()) {
3. super.initInternal(filterConfig);
4. setCasServerLoginUrl(getString(ConfigurationKeys.CAS_SERVER_LOGIN_URL));
5. setRenew(getBoolean(ConfigurationKeys.RENEW));
6. setGateway(getBoolean(ConfigurationKeys.GATEWAY));
7. <!--做好自己!eguid原创-->
8. final String ignorePattern = getString(ConfigurationKeys.</span><span style="color:#ff0000;">IGNORE_PATTERN</span><span style="color:#24292e;">);
9. final String ignoreUrlPatternType = getString(ConfigurationKeys.</span><span style="color:#ff0000;">IGNORE_URL_PATTERN_TYPE</span><span style="color:#24292e;">);
10.
11. if (ignorePattern != null) {
12. final Class<? extends UrlPatternMatcherStrategy> ignoreUrlMatcherClass = PATTERN_MATCHER_TYPES.get(ignoreUrlPatternType);
13. if (ignoreUrlMatcherClass != null) {
14. this.ignoreUrlPatternMatcherStrategyClass = ReflectUtils.newInstance(ignoreUrlMatcherClass.getName());
15. else {
16. try {
17. "Assuming {} is a qualified class name...", ignoreUrlPatternType);
18. this.ignoreUrlPatternMatcherStrategyClass = ReflectUtils.newInstance(ignoreUrlPatternType);
19. catch (final IllegalArgumentException e) {
20. "Could not instantiate class [{}]", ignoreUrlPatternType, e);
21. }
22. }
23. if (this.ignoreUrlPatternMatcherStrategyClass != null) {
24. this.ignoreUrlPatternMatcherStrategyClass.setPattern(ignorePattern);
25. }
26. }
27.
28. final Class<? extends GatewayResolver> gatewayStorageClass = getClass(ConfigurationKeys.GATEWAY_STORAGE_CLASS);
29.
30. if (gatewayStorageClass != null) {
31. setGatewayStorage(ReflectUtils.newInstance(gatewayStorageClass));
32. }
33.
34. final Class<? extends AuthenticationRedirectStrategy> authenticationRedirectStrategyClass = getClass(ConfigurationKeys.AUTHENTICATION_REDIRECT_STRATEGY_CLASS);
35.
36. if (authenticationRedirectStrategyClass != null) {
37. this.authenticationRedirectStrategy = ReflectUtils.newInstance(authenticationRedirectStrategyClass);
38. }
39. }
40. }</span>
虽然使用了反射,但是依然不影响我们找到根本所在,找到ConfigurationKeys这个类里面的变量究竟是什么:
1. <span style="color:#24292e;"> ConfigurationKey<String> IGNORE_PATTERN = new ConfigurationKey<String>("</span><span style="color:#ff0000;">ignorePattern</span><span style="color:#24292e;">", null);
2. 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类用于处理正则验证匹配
1. public final class RegexUrlPatternMatcherStrategy implements UrlPatternMatcherStrategy {
2. <!--做好自己!eguid原创-->
3. private Pattern pattern;
4.
5. public RegexUrlPatternMatcherStrategy() {}
6.
7. public RegexUrlPatternMatcherStrategy(final String pattern) {
8. this.setPattern(pattern);
9. }
10.
11. public boolean matches(final String url) {
12. return this.pattern.matcher(url).find();
13. }
14.
15. public void setPattern(final String pattern) {
16. this.pattern = Pattern.compile(pattern);
17. }
18. }
该类中用到了Pattern来编译和匹配正则表达式
到这里我们终于可以确定可以用ignorePattern来忽略/排除我们不需要拦截的请求URL,当然必须满足正则表达式。
做好自己!--eguid