shiro与cas可以实现无缝的整合,只需要加入几个依赖的jar包,修改部分shiro的配置文件,定义一个自己的casRealm
项目结构
一、在pom中加入cas相关依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-cas</artifactId>
<version>${cas-version}</version>
</dependency>
二、修改shiro配置文件
主要修改三处:
1、shiroFilter
,一是将loginUrl
的value
设置为:cas认证服务地址/login?service=客户端访问地址/cas
,二是配置新增的casFilter
,和固定的拦截规则:/cas = cas
修改如下:
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!-- 构建securityManager环境 -->
<property name="securityManager" ref="securityManager" />
<!-- 要求登录时的链接(可根据项目的URL进行替换),非必须的属性,默认会自动寻找Web工程根目录下的"/login.jsp"页面 -->
<property name="loginUrl" value="https://sso.siwash.net:8443/login?service=http://client.siwash.net:8081/cas"/>
<!--<!– 登录成功后要跳转的连接 –>-->
<!--<property name="successUrl" value="/index.html"/>-->
<!--<!– 没有权限返回的地址 (拒绝访问路径)–>-->
<!--<property name="unauthorizedUrl" value="/index.jsp" />-->
<property name="filters">
<util:map>
<entry key="cas" value-ref="casFilter"/>
<entry key="logoutFilter" value-ref="logoutFilter"/>
</util:map>
</property>
<property name="filterChainDefinitions">
<value>
/casFailure.jsp=anon
/cas = cas
/index.jsp=anon
/index.html=anon
/**=authc
/logout = logoutFilter
/users/** = user
</value>
</property>
</bean>
2.新增一个casFilter:
<bean id="casFilter" class="org.apache.shiro.cas.CasFilter">
<property name="failureUrl" value="http://client.siwash.net:8081/casFailure.jsp" />
<property name="successUrl" value="http://client.siwash.net:8081/zone.html" />
</bean>
3.定义自己的casRealm
,其中casServerUrlPrefix
为cas服务端地址,casService
为客户端地址,这里配置这两个的目的是:在应用A已经认证后应用B访问时,则会直接进入该casRealm,在代码中回去请求认证中心如下url:
https://sso.siwash.net:8443/cas/serviceValidate?ticket=xxxx&service=客户端地址
去验证有效性,并返回用户的信息,当验证通过后应用B则直接进入B系统,而不需要进行二次登陆。
<bean id="realm" class="rpf.study.shiro.casRealm">
<property name="casServerUrlPrefix" value="https://sso.siwash.net:8443/"/>
<property name="casService" value="http://client.siwash.net:8081/cas"/>
</bean>
三、定义自己的casRealm
public class casRealm extends CasRealm{
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
CasToken casToken = (CasToken)token;
if (token == null) {
return null;
} else {
String ticket = (String)casToken.getCredentials();
if (!StringUtils.hasText(ticket)) {
return null;
} else {
TicketValidator ticketValidator = this.ensureTicketValidator();
try {
Assertion casAssertion = ticketValidator.validate(ticket, this.getCasService());
AttributePrincipal casPrincipal = casAssertion.getPrincipal();
String userId = casPrincipal.getName();
Map<String, Object> attributes = casPrincipal.getAttributes();
casToken.setUserId(userId);
String rememberMeAttributeName = this.getRememberMeAttributeName();
String rememberMeStringValue = (String)attributes.get(rememberMeAttributeName);
boolean isRemembered = rememberMeStringValue != null && Boolean.parseBoolean(rememberMeStringValue);
if (isRemembered) {
casToken.setRememberMe(true);
}
List<Object> principals = CollectionUtils.asList(new Object[]{userId, attributes});
PrincipalCollection principalCollection = new SimplePrincipalCollection(principals, this.getName());
return new SimpleAuthenticationInfo(principalCollection, ticket);
} catch (TicketValidationException var14) {
throw new CasAuthenticationException("Unable to validate ticket [" + ticket + "]", var14);
}
}
}
}
}
这里只是将父类的方法直接复制了过来,实际开发中可以将代码中attributes
中读取到的用户数据放入shiro
的session
中去。可以看出这里同样用到了validate
方法去请求cas验证ticket的有效性