今天因为需求做一个 cas (取名CC)引入微信登录.刚好公司自己用sping security 做过一个 oauth 服务器(取名 SOA).于是就用该服务器做.服务器和客户端的验证!
#CAS 4.0 的官方文档# 加入依赖 "cas-server-support-pac4j"4.0 和 "pac4j-oauth" 1.4.1
需要配置一堆东西
- 一个client 不知怎么写先用个现成的 org.pac4j.oauth.client.CasOAuthWrapperClient (这个是用cas做oauth服务器是使用的)
- 然后配入 <bean id="clients" class="org.pac4j.core.client.Clients" ....
- 配置登录时候的拦截 login-webflow.xml 中 写到其他 action 之前 <action-state id="clientAction"..... 以及拦截 bean org.jasig.cas.support.pac4j.web.flow.ClientAction
- oauth 登陆认证的句柄 deployerConfigContext.xml 中 <bean id="primaryAuthenticationHandler" class="org.jasig.cas.support.pac4j.authentication.handler.support.ClientAuthenticationHandler"....
- 将上面的 bean 添加到认证管理里去. deployerConfigContext.xml 中 <bean id="authenticationManager" class="org.jasig.cas.authentication.PolicyBasedAuthenticationManager" ....
- 上面中 #authenticationManager# 中 #PrincipalResolver# 可以为 null! 如: <entry key-ref="primaryAuthenticationHandler"><null /></entry>
- 现在就可以将登陆链接地址 写到登陆页面( 一般是casLoginView.jsp)去了. <a href="${CasOAuthWrapperClientUrl}">OAuth2.0 Login</a>
- CasOAuthWrapperClientUrl 这个名字是根据 第一步中的类名来定义的,加个Url后缀就行了!
我们前面第一步有个坑,现在开始填坑!
- 点击链接并去登陆跳回回调地址 立即出错
- 正常第一步带code参数返回回调地址(cas的login页面),被上面<3>中的Action拦截.该action获取 code 参数,然后去拿 access-token.
- 错误1: # '{"error":"unauthorized","error_description":"Full authentication is required to access this resource"}'# 检查一下 配置的 key 和 secret 有没有错,我配置出错了!
- 修改正确后是是同样的错误!!! 去SOA服务器看 accesslog (当然可以抓包,我们服务器是https的无法看到数据包的内容)
- 发现key secret 作为 get 参数传递了同时没有传递 scope 参数.在 spring security 中 要将key secret 放到请求头里去. 即:Authorization : Basic base64(_DEV_CLIENT_ID_:_DEV_CLIENT_SECRETE_)
- 接下来找怎么修改这个请求呢
- 错误堆栈中 第一行 (TokenExtractor20Impl.java:33) 断点调试跟踪!查看调用链,父调用链中有代码 final Response response = request.send(); 就是他了!取一个合适的名字多么的重要
- 一般不能确定是不是这行代码,那么在这段代码前面打个断点,重新请求一次,多观察一下.如果不是就接着重复!父调用链.
- 继续查看父调用,# this.service.getAccessToken(null, clientVerifier) # 是 this 对象的一个属性 this 是 #CasOAuthWrapperClient#
- 查看该类 this.service = new ExtendedOAuth20ServiceImpl(...); 我们复写掉他就可以了!
- 我们 创建个新类 # MyOAuthClient extend CasOAuthWrapperClient # 覆写掉该语句所在的方法(protected void internalInit() {)原代码直接复制粘贴.同时修改spring配置文件替换 CasOAuthWrapperClient, 修改登陆的url
- 对于#new ExtendedOAuth20ServiceImpl(...)#直接匿名的内部. 覆写掉 #public Token getAccessToken(Token requestToken, Verifier verifier) {#原代码直接复制粘贴.加入一个scope参数和一个header!
- 重启继续出错!#Can't extract a token from this: '<html><head><title>Apache Tomcat/7.# 拿到一个奇怪的内容.继续查看 accesslog 发现看看访问的地址, 竟然是 ../oauth/accessToken,而我的服务器获取token地址是 /oauth/token.. 在什么地方修改呢?继续断点 就在刚才发送http请求的地方吧.
- 发现#final String completeUrl = getCompleteUrl();#completeUrl中就是请求地址看看在哪赋值,发现在new的时候赋值.#this.api.getAccessTokenEndpoint()#new CasOAuthWrapperApi20(this.casOAuthUrl,#public String getAccessTokenEndpoint() {#return this.casServerUrl + "/accessToken?";#
- 直接覆写 匿名内部类 CasOAuthWrapperApi20 #public String getAccessTokenEndpoint() #
- 继续覆写 #AccessTokenExtractor getAccessTokenExtractor()#否则回报 Can't extract a token from this: '{"access_token":"13ff9647-7952-4acd-a2d8-e07277058f42","token#需要json解析
- 或许需要覆写 #public String getAuthorizationUrl(OAuthConfig config) {# 认证地址
- 获取用户信息的地址需要修改 #protected String CasOAuthWrapperClient.getProfileUrl#这个方法
- 解析oath的登陆用户的信息修改 #protected CasOAuthWrapperProfileCasOAuthWrapperClient.extractUserProfile(String body) #
- 到此用户信息就获取完成了!接下来就是插入用户数据到数据库 完成用户登陆
- 最后就是代码的优化了!查看 CasOAuthWrapperClient 的父类 BaseOAuth20Client 的所有实现子类 其中有 google facebook GitHub.
- 根据他们的代码自己优化一下 大功告成! 其实完全可以 基于 BaseOAuth20Client 类进行改造!