文档章节

CAS二次开发记录

kentxp
 kentxp
发布于 2017/09/07 15:27
字数 1283
阅读 79
收藏 0

 

创建项目

下载cas4.1.10的源代码,里面有非常多的module,我们使用cas-server-webapp来作为模块进行二次开发。

自己创建一个项目,将cas-server-webapp拷贝相关文件过来,注意整理pom的依赖。

数据源的修改

我们这里数据源使用的是jndi的方式,所以修改 deployerConfigContext.xml  增加如下配置

	<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
	  	<property name="jndiName"><value>java:comp/env/jdbc/OracleXXXX</value></property>
	</bean>

同时,在webapp/META-INF/context.xml中配置对应的数据源信息

<?xml version="1.0" encoding="UTF-8"?>
<Context>
<Resource name="jdbc/OracleXXXX" 
	  auth="Container" 
	  type="javax.sql.DataSource"
	  maxActive="10"
	  maxIdle="5"
	  maxWait="1000"
	  logAbandoned="true" 
	  username="XXXX"
	  password="XXXX"
	  driverClassName="oracle.jdbc.driver.OracleDriver"
	  url="jdbc:oracle:thin:@192.168.0.1:1521:xxxx"/>
	  
</Context>

考虑到数据的操作,我封装了一个DAO方法,顺便定义一下

	<bean id="casDao"
		class="com.xxx.xxx.sso.CasDao"
	    p:dataSource-ref="dataSource" />

接入系统的配置

哪些系统能够使用cas,默认的定义在

 <bean id="serviceRegistryDao" class="org.jasig.cas.services.JsonServiceRegistryDao"
          c:configDirectory="${service.registry.config.location:classpath:services}" />

cas支持4中接入方式的定义:

1、InMemoryServiceRegistryDaoImpl
2、JsonServiceRegistryDao
3、JpaServiceRegistryDaoImpl:
4、MongoServiceRegistryDao

考虑到实际的业务场景,这部分的数据,肯定是需要配置到数据库中的,而且能够自动刷新,支持新增新的service时,能够做到不停机。

所以我们需要自己去实现,定义如下

<bean id="serviceRegistryDao" class="com.xxxx.xxxx.sso.DbServiceRegistryDaoImpl"
  p:casDao-ref="casDao" />
public class DbServiceRegistryDaoImpl implements ServiceRegistryDao {
    @NotNull
    private CasDao casDao;

    public DbServiceRegistryDaoImpl() {
    }

    public void setCasDao(CasDao casDao) {
        this.casDao = casDao;
    }

    @Override
    public RegisteredService save(RegisteredService registeredService) {
        return null;
    }

    @Override
    public boolean delete(RegisteredService registeredService) {
        return false;
    }

    @Override
    public List<RegisteredService> load() {
        return casDao.findRegisteredService();
    }

    @Override
    public RegisteredService findServiceById(long l) {
        return casDao.findRegisteredServiceByServiceId(l);
    }
}

我们定义一个类去继承ServiceRegistryDao  主要去实现load和findServiceById的方法,暂时不实现delete和save。

主要是从数据库获取数据  返回对应的对象

	public List<RegisteredService> findRegisteredService() {
		List<RegisteredService> registeredServices = jdbcTemplate.query("select * from T_SYSTEM  ",
				new Object[] {}, new int[] {}, new RowMapper<RegisteredService>() {
					@Override
					public RegisteredService mapRow(ResultSet rs, int rowNum) throws SQLException {
						RegexRegisteredService registeredServices = new RegexRegisteredService();
						registeredServices.setServiceId("^"+rs.getString("redirect_uri")+".*");
						registeredServices.setName(rs.getString("systemname"));
						registeredServices.setDescription(rs.getString("systemdes"));
						registeredServices.setId(rs.getLong("SYSTEMID"));
						List<String> allowedAttributes = new ArrayList<String>();
						allowedAttributes.add(rs.getString("systemcode"));
						registeredServices
								.setAttributeReleasePolicy(new ReturnAllowedAttributeReleasePolicy(allowedAttributes));
						return registeredServices;
					}
				});
		return registeredServices;
	}

    RegisteredService是一个接口,有两个实现类,我们使用 RegexRegisteredService  ,该实现类支持RegisteredService的正则匹配。

    allowedAttributes 很重要,后面我们将用它来过滤返回给接入服务的attribute。
 

修改登录方法和根据业务系统返回不同数据

登录的方法是定义在 deployerConfigContext.xml  的

<bean id="authenticationManager"
   class="org.jasig.cas.authentication.PolicyBasedAuthenticationManager">
   <constructor-arg>
      <map>
         <entry key-ref="proxyAuthenticationHandler" value-ref="proxyPrincipalResolver" />
         <entry key-ref="primaryAuthenticationHandler" value-ref="primaryPrincipalResolver" />
      </map>
   </constructor-arg>
   <property name="authenticationPolicy">
      <bean class="org.jasig.cas.authentication.AnyAuthenticationPolicy" />
   </property>
</bean>

其中primaryAuthenticationHandler控制的是登录认证 ,primaryPrincipalResolver是返回的属性

primaryAuthenticationHandler 修改如下

	<bean id="primaryAuthenticationHandler"
		class="org.jasig.cas.adaptors.jdbc.QueryDatabaseAuthenticationHandler"
		p:dataSource-ref="dataSource" p:passwordEncoder-ref="MD5PasswordEncoder"
		p:sql="select PWD from T_USER where USERACCOUNT = ?" />

	<bean id="MD5PasswordEncoder"
		class="org.jasig.cas.authentication.handler.DefaultPasswordEncoder">
		<constructor-arg index="0">
			<value>MD5</value>
		</constructor-arg>
	</bean>

因为我们认证相对比较简单,这里使用QueryDatabaseAuthenticationHandler去处理登录时候的认证

主要是primaryPrincipalResolver的处理,这里是处理的认证成功后,返回业务系统的用户的数据,默认只返回了userId,我们需要返回更多的数据,这里自己定义一个类来处理

	<bean id="primaryPrincipalResolver"
		class="com.xxx.xxx.sso.MyPrincipalResolver"
	    p:casDao-ref="casDao" />

public class MyPrincipalResolver implements PrincipalResolver {
    protected final Logger logger = LoggerFactory.getLogger(this.getClass());

	@NotNull
    private CasDao casDao;
	
	
	@Override
	public Principal resolve(Credential credential) {
		
        final MyUsernamePasswordCredentials usernamePasswordCredentials = (MyUsernamePasswordCredentials) credential;
        String userStr = "";
        Map<String,Object> attr = new HashMap<String,Object>();

        try {
            String username = usernamePasswordCredentials.getUsername();
            userStr = username;
            User user = casDao.queryUser(username);

            for(Role role : user.getRoles()){
            	Map<String,Object> systemAttr = new HashMap<String,Object>();
                //TODO 设置要返回给接入服务的属性
            	attr.put(role.getSystemCode(), systemAttr);
            }
            
            return new SimplePrincipal(userStr, attr);

        } catch (Exception e) {
        	logger.error("获取用户属性异常{}",e);
        } 
        return null;
	}

	@Override
	public boolean supports(Credential credential) {
		return true;
	}

	public void setCasDao(CasDao casDao) {
		this.casDao = casDao;
	}
	
}

    这里描述一下业务场景

        我们经常遇到如下需求,N个系统接入CAS;

        对于同一个用户,N个系统可能有不同的属性信息(比如针对每个系统用户的权限信息),每个系统之间,这类信息应该做到隔离。

       所以我们在用户登录后,构造一个Map, 使用接入系统的代码去做key,想想前面我们在构造registeredServices是,设置的

setAttributeReleasePolicy(new ReturnAllowedAttributeReleasePolicy(allowedAttributes));

cas会根据这个去过滤相关的属性。

    有人问 ,为什么要全取出来 ,而不是根据登录系统的情况,每次动态获取呢?

    可以测试一下,登录完A系统后,如果用户直接点击B系统,CAS是不会进入这个方法的。

    修改对应的protocol模板,返回属性,由于我们接入系统,使用的是 shiro cas那个包,支持的是2.0的协议,所以修改view/protocol/2.0/casServiceValidationSuccess.jsp

<%@ page session="false" contentType="application/xml; charset=UTF-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
<cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'>
    <cas:authenticationSuccess>
        <cas:user>${fn:escapeXml(principal.id)}</cas:user>
         <!-- 这段 -->  
        <c:if test="${fn:length(assertion.primaryAuthentication.principal.attributes) > 0}">
            <cas:attributes>  
                <c:forEach var="attr" items="${assertion.primaryAuthentication.principal.attributes}">  
                	<c:forEach var="systemAttr" items="${attr.value}">  
                		<cas:${fn:escapeXml(systemAttr.key)}>${fn:escapeXml(systemAttr.value)}</cas:${fn:escapeXml(systemAttr.key)}>
                    </c:forEach>       
                </c:forEach>  
            </cas:attributes>  
        </c:if>  
        <!-- 这段 end--> 
        <c:if test="${not empty pgtIou}">
            <cas:proxyGrantingTicket>${pgtIou}</cas:proxyGrantingTicket>
        </c:if>
        <c:if test="${fn:length(chainedAuthentications) > 0}">
            <cas:proxies>
                <c:forEach var="proxy" items="${chainedAuthentications}" varStatus="loopStatus" begin="0" end="${fn:length(chainedAuthentications)}" step="1">
                    <cas:proxy>${fn:escapeXml(proxy.principal.id)}</cas:proxy>
                </c:forEach>
            </cas:proxies>
        </c:if>
    </cas:authenticationSuccess>
</cas:serviceResponse>

 

© 著作权归作者所有

共有 人打赏支持
kentxp
粉丝 2
博文 7
码字总数 5393
作品 0
合肥
PHP-CAS-Server—基于Laravel的CAS服务端实现

PHP CAS Server是一个基于Laravel框架开发的CAS服务端实现,旨在解决使用PHP技术栈的中小型公司因无法对Java版CAS服务端二次开发而放弃使用CAS的问题,因此本项目的核心目标之一就是易于扩展...

局长
2016/10/23
19
0
基于 Laravel 的 CAS 服务端实现--PHP-CAS-Server

PHP CAS Server是一个基于Laravel框架开发的CAS服务端实现,旨在解决使用PHP技术栈的中小型公司因无法对Java版CAS服务端二次开发而放弃使用CAS的问题,因此本项目的核心目标之一就是易于扩展...

leo108
2016/10/20
2.4K
1
CAS Server 下载、部署、测试 与 Tomcat 7.0 开启 Https

这篇文章只介绍如何下载、部署、测试,概念将在单独篇章进行讲解。 值得注意的是cas server 没有现成的应用部署包,是需要我们自己去打包成war后,手动进行发布,有两种打包方式,第一种是通...

华山猛男
03/12
1
0
leo108/php_cas_server

简介 PHP CAS Server是一个基于Laravel框架开发的CAS服务端实现,旨在解决使用PHP技术栈的中小型公司因无法对Java版CAS服务端二次开发而放弃使用CAS的问题,因此本项目的核心目标之一就是易于...

leo108
2016/10/30
0
0
浅谈Java并发编程系列(七) —— 深入解析synchronized关键字

Synchronized关键字 synchronized的锁机制的主要优势是Java语言内置的锁机制,因此,JVM可以自由的优化而不影响已存在的代码。 任何对象都拥有对象头这一数据结构来支持锁,但是对于较大的对...

codershamo
2017/11/29
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Shiro | 实现权限验证完整版

写在前面的话 提及权限,就会想到安全,是一个十分棘手的话题。这里只是作为学校Shiro的一个记录,而不是,权限就应该这样设计之类的。 Shiro框架 1、Shiro是基于Apache开源的强大灵活的开源...

冯文议
今天
1
0
linux 系统的运行级别

运行级别 运行级别 | 含义 0 关机 1 单用户模式,可以想象为windows 的安全模式,主要用于修复系统 2 不完全的命令模式,不含NFS服务 3 完全的命令行模式,就是标准的字符界面 4 系统保留 5 ...

Linux学习笔记
今天
2
0
学习设计模式——命令模式

任何模式的出现,都是为了解决一些特定的场景的耦合问题,以达到对修改封闭,对扩展开放的效果。命令模式也不例外: 命令模式是为了解决命令的请求者和命令的实现者之间的耦合关系。 解决了这...

江左煤郎
今天
3
0
字典树收集(非线程安全,后续做线程安全改进)

将500W个单词放进一个数据结构进行存储,然后进行快速比对,判断一个单词是不是这个500W单词之中的;来了一个单词前缀,给出500w个单词中有多少个单词是该前缀. 1、这个需求首先需要设计好数据结...

算法之名
昨天
15
0
GRASP设计模式

此文参考了这篇博客,建议读者阅读原文。 面向对象(Object-Oriented,OO)是当下软件开发的主流方法。在OO分析与设计中,我们首先从问题领域中抽象出领域模型,在领域模型中以适当的粒度归纳...

克虏伯
昨天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部