SpringBoot整合CAS登录

一、准备工作

  1. 本地准备cas服务端(https://github.com/apereo/cas),下载cas代码并build成war包

  1. 本地准备Tomcat,并将打包好的cas包放至tomcat的webapps目录下,启动Tomcat

  1. 打开WEB-INF下application.properties文件,加入以下配置

#使用http协议
cas.tgc.secure=false
cas.serviceRegistry.initFromJson=true
#退出登录后允许跳转
cas.logout.followServiceRedirects=true

#修改原本账号密码
cas.authn.accept.users=admin::123456
#修改端口号
server.port=8080
  1. 打开WEB-INF下services下HTTPSandIMAPS-10000001.json文件,修改以下配置。CAS默认使用的是https协议,我们修改让其支持http协议

  1. 启动Tomcat,测试是否部署成功

二、项目整合客户端

  1. 引入cas客户端依赖

<!--cas客户端-->
<dependency>
    <groupId>com.cas</groupId>
    <artifactId>cas-client-core</artifactId>
    <version>3.2.1</version>
</dependency>
  1. 编写cas拦截器的配置类(建议地址写在配置文件中)

package com.dibo.edu.cas;

import org.jasig.cas.client.session.SingleSignOutFilter;
import org.jasig.cas.client.util.HttpServletRequestWrapperFilter;
import org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletContextInitializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.SessionTrackingMode;
import java.util.*;

@Configuration
public class CasConfig {

    @Bean
    public ServletContextInitializer servletContextInitializer1() {
        return new ServletContextInitializer() {
            @Override
            public void onStartup(ServletContext servletContext) throws ServletException {
                servletContext.setSessionTrackingModes(Collections.singleton(SessionTrackingMode.COOKIE) );
            }
        };
    }


    /**
     * 用于实现单点登出功能
     */
    @Bean
    public FilterRegistrationBean<SingleSignOutFilter> logOutFilter() {
        FilterRegistrationBean<SingleSignOutFilter> authenticationFilter = new FilterRegistrationBean();
        authenticationFilter.setFilter(new SingleSignOutFilter());
        authenticationFilter.addUrlPatterns("/*");
        authenticationFilter.setOrder(1);
        return authenticationFilter;
    }

    //配置认证Filter
    @Bean
    public FilterRegistrationBean authenticationFilterRegistrationBean() {
        FilterRegistrationBean authenticationFilter = new FilterRegistrationBean();
        authenticationFilter.setFilter(new AuthenticationFilter());
        Map<String, String> initParameters = new HashMap<String, String>();
        initParameters.put("casServerLoginUrl", "http://192.168.1.17:8080/cas/login");
        initParameters.put("serverName", "http://192.168.1.17:8888");
        //CAS过滤器白名单的设置,不同版本名称不同,可点进AuthenticationFilter进行查看
//        initParameters.put("casWhiteUrl","/cas/casLogout");
        authenticationFilter.setInitParameters(initParameters);
        authenticationFilter.setOrder(2);
        List<String> urlPatterns = new ArrayList<String>();
        urlPatterns.add("/*");// 设置匹配的url
        authenticationFilter.setUrlPatterns(urlPatterns);
        return authenticationFilter;
    }

    //配置ticket验证Filter
    @Bean
    public FilterRegistrationBean ValidationFilterRegistrationBean(){
        FilterRegistrationBean authenticationFilter = new FilterRegistrationBean();
        authenticationFilter.setFilter(new Cas20ProxyReceivingTicketValidationFilter());
        Map<String, String> initParameters = new HashMap<String, String>();
        initParameters.put("casServerUrlPrefix", "http://192.168.1.17:8080/cas");
        initParameters.put("serverName", "http://192.168.1.17:8888");
        authenticationFilter.setInitParameters(initParameters);
        authenticationFilter.setOrder(1);
        List<String> urlPatterns = new ArrayList<String>();
        urlPatterns.add("/*");// 设置匹配的url
        authenticationFilter.setUrlPatterns(urlPatterns);
        return authenticationFilter;
    }


    //配置获取用户信息的Filter
    //request.getRemoteUser()
    @Bean
    public FilterRegistrationBean casHttpServletRequestWrapperFilter(){
        FilterRegistrationBean authenticationFilter = new FilterRegistrationBean();
        authenticationFilter.setFilter(new HttpServletRequestWrapperFilter());
        authenticationFilter.setOrder(3);
        List<String> urlPatterns = new ArrayList<String>();
        urlPatterns.add("/*");// 设置匹配的url
        authenticationFilter.setUrlPatterns(urlPatterns);
        return authenticationFilter;
    }

}
  1. 编写cas相关controller

@RequestMapping("/cas/casLogin")
public void casLogin(HttpServletRequest request, HttpServletResponse response) throws IOException {
        String username = request.getRemoteUser();
        log.info("登录用户账号:--------->{}", username);
        String sessionId = request.getSession().getId();
        log.info("sessionId:--------->{}", sessionId);

        #撰写项目中逻辑(我这边是根据cas登录用户名获取系统登录token)
        //获取token
        String authToken = AuthServiceFactory.getAuthService(Cons.DICTCODE_AUTH_TYPE.PWD.name()).applyToken(credential);
        log.info("token------->{}",authToken);
        #重定向至前端页面(携带系统token)
//        response.sendRedirect("http://210.28.145.114/ms/user/login?authToken=" + authToken);
        response.sendRedirect("http://localhost:8000/user/login?authToken=" + authToken);
    }
  1. 前端请求接口,你会发现浏览器地址栏发生变化,登录成功后cas会自动回调casLogin这个方法

  1. 输入账号密码登录出现跨域情况,重写cas包下过滤器(由于前端是ajax请求,这时候我们需要将接口进行标记返回至前端处理)

package com.dibo.edu.cas;//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//


import java.io.IOException;
import java.net.URLEncoder;
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 javax.servlet.http.HttpSession;

import org.jasig.cas.client.authentication.DefaultGatewayResolverImpl;
import org.jasig.cas.client.authentication.GatewayResolver;
import org.jasig.cas.client.util.AbstractCasFilter;
import org.jasig.cas.client.util.CommonUtils;
import org.jasig.cas.client.validation.Assertion;

public class AuthenticationFilter extends AbstractCasFilter {
    private String casServerLoginUrl;
    private boolean renew = false;
    private boolean gateway = false;
    private GatewayResolver gatewayStorage = new DefaultGatewayResolverImpl();

    public AuthenticationFilter() {
    }

    protected void initInternal(FilterConfig filterConfig) throws ServletException {
        if (!this.isIgnoreInitConfiguration()) {
            super.initInternal(filterConfig);
            this.setCasServerLoginUrl(this.getPropertyFromInitParams(filterConfig, "casServerLoginUrl", (String) null));
            this.log.trace("Loaded CasServerLoginUrl parameter: " + this.casServerLoginUrl);
            this.setRenew(this.parseBoolean(this.getPropertyFromInitParams(filterConfig, "renew", "false")));
            this.log.trace("Loaded renew parameter: " + this.renew);
            this.setGateway(this.parseBoolean(this.getPropertyFromInitParams(filterConfig, "gateway", "false")));
            this.log.trace("Loaded gateway parameter: " + this.gateway);
            String gatewayStorageClass = this.getPropertyFromInitParams(filterConfig, "gatewayStorageClass", (String) null);
            if (gatewayStorageClass != null) {
                try {
                    this.gatewayStorage = (GatewayResolver) Class.forName(gatewayStorageClass).newInstance();
                } catch (Exception var4) {
                    this.log.error(var4, var4);
                    throw new ServletException(var4);
                }
            }
        }

    }

    public void init() {
        super.init();
        CommonUtils.assertNotNull(this.casServerLoginUrl, "casServerLoginUrl cannot be null.");
    }

    public final void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        HttpSession session = request.getSession(false);
        Assertion assertion = session != null ? (Assertion) session.getAttribute("_const_cas_assertion_") : null;

        if (assertion != null) {
            filterChain.doFilter(request, response);
        } else {
            String serviceUrl = this.constructServiceUrl(request, response);
            String ticket = CommonUtils.safeGetParameter(request, this.getArtifactParameterName());
            boolean wasGatewayed = this.gatewayStorage.hasGatewayedAlready(request, serviceUrl);
            if (!CommonUtils.isNotBlank(ticket) && !wasGatewayed) {
                this.log.debug("no ticket and no assertion found");
                String modifiedServiceUrl;
                if (this.gateway) {
                    this.log.debug("setting gateway attribute in session");
                    modifiedServiceUrl = this.gatewayStorage.storeGatewayInformation(request, serviceUrl);
                } else {
                    modifiedServiceUrl = serviceUrl;
                }

                if (this.log.isDebugEnabled()) {
                    this.log.debug("Constructed service url: " + modifiedServiceUrl);
                }

                String urlToRedirectTo = CommonUtils.constructRedirectUrl(this.casServerLoginUrl, this.getServiceParameterName(), modifiedServiceUrl, this.renew, this.gateway);
                if (this.log.isDebugEnabled()) {
                    this.log.debug("redirecting to \"" + urlToRedirectTo + "\"");
                }
                //判断请求方式是否为ajax请求
                String header = request.getHeader("X-Requested-With");
                if (header != null && "XMLHttpRequest".equals(header)) {
                    //给这个请求打上标记(登录已经超时或者认证未通过)
                    ajaxHttpToLogin(request, response, urlToRedirectTo);
                    return;
                } else {
                    this.log.debug("redirecting to \"" + urlToRedirectTo + "\"");
                    response.sendRedirect(urlToRedirectTo);
                }
//                response.sendRedirect(urlToRedirectTo);
            } else {
                filterChain.doFilter(request, response);
            }
        }
    }


    public final void setRenew(boolean renew) {
        this.renew = renew;
    }

    public final void setGateway(boolean gateway) {
        this.gateway = gateway;
    }

    public final void setCasServerLoginUrl(String casServerLoginUrl) {
        this.casServerLoginUrl = casServerLoginUrl;
    }

    public final void setGatewayStorage(GatewayResolver gatewayStorage) {
        this.gatewayStorage = gatewayStorage;
    }

    /**
     * ajax请求标记
     *
     * @param request
     * @param response
     * @param loginUrl
     */
    private void ajaxHttpToLogin(HttpServletRequest request, HttpServletResponse response, String loginUrl) {
        try {
            response.setHeader("SESSIONSTATUS", "TIMEOUT");
            response.setHeader("CONTEXTPATH", loginUrl);
            response.setStatus(HttpServletResponse.SC_FORBIDDEN);//403 禁止
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
  1. 修改后登录,F12检查接口,处理后即可成功登录(跳转至系统中页面)

  1. 退出登录(先清除原系统的token,后注销session)

 @GetMapping("/casLogout")
    public JsonResult casLogout(HttpSession session, HttpServletResponse response) throws IOException {
        //先执行源系统退出
        IamSecurityUtils.logout();
        //再执行Cas的单点退出功能
        //注销session
        session.invalidate();
        // ids的退出地址  authserver为ids的上下文,logout为固定值
        String casLogoutURL = "http://192.168.1.17:8080/cas/logout";
        // service后面带的参数为应用的访问地址,需要使用URLEncoder进行编码
        String redirectURL = casLogoutURL + "?service=http://192.168.1.17:8888/wxzy-rest/cas/casLogin";
        response.sendRedirect(redirectURL);
        return JsonResult.OK();
    }
  1. 退出成功后(若不配置退出跳转将不会跳转登录页)

退出成功就会回调到casLogin方法,Cas过滤器发现没有登录,就又会跳转到Cas服务器的单点登录页面了

三、总结

关于springboot整合cas的内容分享结束了,具体项目集成成什么样还需具体分析。博主说的也不是很透彻,许多东西是在实践中慢慢摸索出来的。希望这边文章可以帮助到大家。

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值