CAS实现第三方免登录

(本文的介绍是基于CAS4.0的版本)

今天介绍一下单点登录实现无页面的免登,场景是在微信公众号端(第三方登录),用户点击微信授权后,自动登录企业系统。主要涉及的系统,以及交互如下所示:

登录流程

逻辑实现

上面的流程看似复杂,但是我们可以暂时不用看usercenter(用户中心)和微信服务器的交互。这里面主要说一下CAS免登录的接口是如何实现的。还需要说明一点的是,CAS只管登录相关的事情,与第三方系统的交互全部都在usercenter中进行。

HugeAutoLoginController

/**
 * 第三方免登录
 *
 * Created by xuefeihu on 18/4/27.
 *
 */
public class HugeAutoLoginController extends AbstractController {

    private static final Logger LOGGER = LoggerFactory.getLogger(HugeAutoLoginController.class);

    private CentralAuthenticationService centralAuthenticationService;
    private CookieRetrievingCookieGenerator ticketGrantingTicketCookieGenerator;
    private Md5PasswordEncoder md5PasswordEncoder;
    private String casKey = "helloworld";
    /**
     * 用户中心微信认证URL
     */
    private String userWechatUrl;

    @Override
    protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
        try {
            String username = request.getParameter("username");
            String sign = request.getParameter("sign");
            final String service = request.getParameter("service");

            LOGGER.info("username:" + username +"    sign:" +sign+ "   service:" +service);

            String redirectUrl = userWechatUrl + "?service=" + service;
            if (StringUtils.isEmpty(username) || StringUtils.isEmpty(sign) || StringUtils.isEmpty(service)) { // 未通过第三方授权时,跳至usercenter
                return new ModelAndView("redirect:" + redirectUrl);
            }

            LOGGER.info(" my  sign+ " + md5PasswordEncoder.encodePassword(username, casKey));
            if (!sign.equals(md5PasswordEncoder.encodePassword(username, casKey))) {// 免登接口验证失败,也跳转至usercenter
                return new ModelAndView("redirect:" + redirectUrl);
            }

            HugeCredential credential = new HugeCredential();
            credential.setUsername(username);
            credential.setAutoLogin(true);
            // 生成Ticket
            String tgt = centralAuthenticationService.createTicketGrantingTicket(credential);
            ticketGrantingTicketCookieGenerator.addCookie(request, response, tgt);
            final String serviceTicketId = centralAuthenticationService.grantServiceTicket(
                    tgt, new SimpleWebApplicationServiceImpl(service), credential);

            LOGGER.info("自动登录成功!");
            return new ModelAndView("redirect:" + service + "?ticket=" + serviceTicketId);// 需要返回serviceTicket,否则跳转不到cas client的目标页面
        } catch (Exception e) {
            logger.error("自动登录内部异常, e={}", e);
        }
        return null;
    }

    public void setCasKey(String casKey) {
        this.casKey = casKey;
    }

    public void setCentralAuthenticationService(CentralAuthenticationService centralAuthenticationService) {
        this.centralAuthenticationService = centralAuthenticationService;
    }

    public void setMd5PasswordEncoder(Md5PasswordEncoder md5PasswordEncoder) {
        this.md5PasswordEncoder = md5PasswordEncoder;
    }

    public void setTicketGrantingTicketCookieGenerator(CookieRetrievingCookieGenerator ticketGrantingTicketCookieGenerator) {
        this.ticketGrantingTicketCookieGenerator = ticketGrantingTicketCookieGenerator;
    }

    public void setUserWechatUrl(String userWechatUrl) {
        this.userWechatUrl = userWechatUrl;
    }
}

cas-servlet.xml

<!-- 自定义 Handler Mapping -->
<bean id="handlerMappingB" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
    <property name="mappings">
        <props>
            <prop key="/hugeAutoLogin">hugeAutoLoginController</prop>
        </props>
    </property>
    <property name="interceptors">
        <list>
            <ref bean="localeChangeInterceptor" />
        </list>
    </property>
</bean>

...

<!-- 第三方免登录Controller -->
<bean id="hugeAutoLoginController" class="com.moguhu.cas.controller.HugeAutoLoginController">
    <property name="centralAuthenticationService" ref="centralAuthenticationService" />
    <property name="ticketGrantingTicketCookieGenerator" ref="ticketGrantingTicketCookieGenerator" />
    <property name="md5PasswordEncoder" ref="md5PasswordEncoder" />
    <property name="userWechatUrl" value="${usercenter.wechat.authorize}" />
</bean>

web.xml

最后在web.xml中暴露接口:

<servlet-mapping>
    <servlet-name>cas</servlet-name>
    <url-pattern>/hugeAutoLogin</url-pattern>
</servlet-mapping>