CAS客户端的拆离
1. 场景
一般来说都是去集成或者整合CAS,但是今天记录的确是反着来的,即把CAS从现有系统中给剔除掉,不用它了。
遇到一个场景是这样子的:需要将原有的CAS认证体系去除掉(其他团队做的一个门户系统),换做一个另一个简易的认证中心。为了保持系统的扩展性,需要在尽量不修改原来的代码的基础上完成迁移。
2. 思路与分离方法
要尽量少的改动原有代码,那么就从源头上开始修改,也就是去修改过滤器。
- 增加配置开关,根据配置来启用或关闭原CAS所有的filter
- 改造用户验证逻辑
- 新增filter去新的认证中心去验证用户信息
- 改造用户信息获取方式
在此过程中遇到的主要的一个问题是获取用户信息时,原CAS是通过request.getUserPrincipal()
来进行获取的,但是新的认证中心并没有采取这种方式,所以取到的一定是空的,并且request接口中并没有提供request.setUserPrincipal()
来设置认证主体的方法。
最后全局搜索了一下,发现在HttpServletRequest
的实现类Request
中有setUserPrincipal(Principal principal)
方法来设置认证主体,并且通过调试发现,filter中传入的ServletRequest
实际上是RequestFacade
,而RequestFacade
中则存在一个request
字段,于是乎,就可以通过反射来获取到这个Request
的对象实例,从而把认证主体给set进去。
关键代码:
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
Principal logUser;
//验证用户信息,获取用户数据,得到Principal(略)
// 需要注意的是这个Principal的实现类使用AttributePrincipalImpl,因为CAS中使用的是它
// ......
//设置Principal
Class clazz = servletRequest.getClass();
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
String name = field.getName();
if ("request".equals(name)) {
boolean accessible = field.isAccessible();
field.setAccessible(true);
Request req;
try {
req = (Request) field.get(servletRequest);
req.setUserPrincipal(logUser);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
field.setAccessible(accessible);
}
}
}
这样子servletRequest中就可以放入用户信息了:
另一个问题是在验证失败重定向response到登陆页面的时候出现了跨域问题,导致返回的response一直为空,但是浏览器中并没有明确提示是跨域导致的,所以得设置下允许跨域就行。
3. 总结
CAS作为认证中心,功能确实多,并且对各种认认证协议的支持性也很好,但是太过臃肿,很多功能是用不上的,启动也较慢,可控度和自由性也比较低,比如找了半天没找到能够自定义ticket(或者TGT和GT)的生成逻辑(也可能有但还没发现?)所以个人觉得能自己开发还是自己开发个认证中心,全流程自己控制,用着也舒服。
时间真的可以治愈一切吗?不,治愈一切的只能是自己,我想我做到了