今天因为需求做一个 cas (取名CC)引入微信登录.刚好公司自己用sping security 做过一个 oauth 服务器(取名 SOA).于是就用该服务器做.服务器和客户端的验证!

#CAS 4.0 的官方文档#  加入依赖 "cas-server-support-pac4j"4.0 和 "pac4j-oauth" 1.4.1

需要配置一堆东西

  1. 一个client 不知怎么写先用个现成的 org.pac4j.oauth.client.CasOAuthWrapperClient (这个是用cas做oauth服务器是使用的)
  2. 然后配入 <bean id="clients" class="org.pac4j.core.client.Clients" ....
  3. 配置登录时候的拦截 login-webflow.xml 中 写到其他 action 之前 <action-state id="clientAction"..... 以及拦截 bean   org.jasig.cas.support.pac4j.web.flow.ClientAction
  4. oauth 登陆认证的句柄 deployerConfigContext.xml 中 <bean id="primaryAuthenticationHandler" class="org.jasig.cas.support.pac4j.authentication.handler.support.ClientAuthenticationHandler"....
  5. 将上面的 bean 添加到认证管理里去. deployerConfigContext.xml 中 <bean id="authenticationManager" class="org.jasig.cas.authentication.PolicyBasedAuthenticationManager" ....
  6. 上面中 #authenticationManager# 中 #PrincipalResolver# 可以为 null! 如: <entry key-ref="primaryAuthenticationHandler"><null /></entry>
  7. 现在就可以将登陆链接地址 写到登陆页面( 一般是casLoginView.jsp)去了.  <a href="${CasOAuthWrapperClientUrl}">OAuth2.0 Login</a>
  8. CasOAuthWrapperClientUrl 这个名字是根据 第一步中的类名来定义的,加个Url后缀就行了!

我们前面第一步有个坑,现在开始填坑!

  1. 点击链接并去登陆跳回回调地址 立即出错
  2. 正常第一步带code参数返回回调地址(cas的login页面),被上面<3>中的Action拦截.该action获取 code 参数,然后去拿 access-token.
  3. 错误1: # '{"error":"unauthorized","error_description":"Full authentication is required to access this resource"}'# 检查一下 配置的 key 和 secret 有没有错,我配置出错了!
  4. 修改正确后是是同样的错误!!! 去SOA服务器看 accesslog (当然可以抓包,我们服务器是https的无法看到数据包的内容)
  5. 发现key secret 作为 get 参数传递了同时没有传递 scope 参数.在 spring security 中 要将key secret  放到请求头里去. 即:Authorization : Basic base64(_DEV_CLIENT_ID_:_DEV_CLIENT_SECRETE_)
  6. 接下来找怎么修改这个请求呢
  7. 错误堆栈中 第一行 (TokenExtractor20Impl.java:33) 断点调试跟踪!查看调用链,父调用链中有代码 final Response response = request.send(); 就是他了!取一个合适的名字多么的重要
  8. 一般不能确定是不是这行代码,那么在这段代码前面打个断点,重新请求一次,多观察一下.如果不是就接着重复!父调用链.
  9. 继续查看父调用,# this.service.getAccessToken(null, clientVerifier) # 是 this 对象的一个属性 this 是 #CasOAuthWrapperClient#
  10. 查看该类 this.service = new ExtendedOAuth20ServiceImpl(...); 我们复写掉他就可以了!
  11. 我们 创建个新类 # MyOAuthClient extend CasOAuthWrapperClient # 覆写掉该语句所在的方法(protected void internalInit() {)原代码直接复制粘贴.同时修改spring配置文件替换 CasOAuthWrapperClient, 修改登陆的url
  12. 对于#new ExtendedOAuth20ServiceImpl(...)#直接匿名的内部. 覆写掉 #public Token getAccessToken(Token requestToken, Verifier verifier) {#原代码直接复制粘贴.加入一个scope参数和一个header!
  13. 重启继续出错!#Can't extract a token from this: '<html><head><title>Apache Tomcat/7.# 拿到一个奇怪的内容.继续查看 accesslog 发现看看访问的地址, 竟然是 ../oauth/accessToken,而我的服务器获取token地址是 /oauth/token.. 在什么地方修改呢?继续断点 就在刚才发送http请求的地方吧.
  14. 发现#final String completeUrl = getCompleteUrl();#completeUrl中就是请求地址看看在哪赋值,发现在new的时候赋值.#this.api.getAccessTokenEndpoint()#new CasOAuthWrapperApi20(this.casOAuthUrl,#public String getAccessTokenEndpoint() {#return this.casServerUrl + "/accessToken?";#
  15. 直接覆写 匿名内部类 CasOAuthWrapperApi20 #public String getAccessTokenEndpoint() #
  16. 继续覆写 #AccessTokenExtractor getAccessTokenExtractor()#否则回报 Can't extract a token from this: '{"access_token":"13ff9647-7952-4acd-a2d8-e07277058f42","token#需要json解析
  17. 或许需要覆写 #public String getAuthorizationUrl(OAuthConfig config) {# 认证地址
  18. 获取用户信息的地址需要修改 #protected String CasOAuthWrapperClient.getProfileUrl#这个方法
  19. 解析oath的登陆用户的信息修改 #protected CasOAuthWrapperProfileCasOAuthWrapperClient.extractUserProfile(String body) #
  20. 到此用户信息就获取完成了!接下来就是插入用户数据到数据库 完成用户登陆
  21. 最后就是代码的优化了!查看 CasOAuthWrapperClient 的父类 BaseOAuth20Client 的所有实现子类 其中有 google facebook GitHub.
  22. 根据他们的代码自己优化一下 大功告成! 其实完全可以 基于 BaseOAuth20Client 类进行改造!