一、前言
Apache Shiro与Spring Security一样是Java的一个安全框架。那为什么与Spring整合却用Shiro?其实我个人是认为Spring Security太过于笨重,要写太多的过滤器,Shiro的配置简单这就是我选择的理由,何况Spring官方自己都推荐使用Shiro。Shiro最主要的就是认证与授权,而CAS的重点在于单点登录,其实CAS与Shiro整合的话就是关于认证那块的整合。
二、配置
第一步、添加Maven依赖
<!-- shiro依赖包 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.2.4</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.2.3</version>
</dependency>
<!-- shiro-cas集成依赖包 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-cas</artifactId>
<version>1.2.3</version>
</dependency>
第二步、web.xml中添加shiro过滤器
<!-- shiro权限过滤 (此配置一定要在别的配置之前) -->
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
第三步、添加applicationContext-shiro.xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd">
<description>Shiro 配置</description>
<!-- CasFilter为自定义的单点登录Fileter -->
<bean id="casFilter" class="org.apache.shiro.cas.CasFilter">
<!-- 配置验证错误时的失败页面 -->
<property name="failureUrl" value="/static/no-permission.jsp"/>
</bean>
<!--shiroFilter的bean Name必须与web.xml中shiro filter的名字一致-->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!-- Shiro的核心安全接口,这个属性是必须的 -->
<property name="securityManager" ref="securityManager"/>
<!-- 要求登录时的链接(可根据项目的URL进行替换),非必须的属性,默认会自动寻找Web工程根目录下的"/login.jsp"页面 -->
<property name="loginUrl" value="http://cas.uat.qa.nt.ctripcorp.com/caso/login?service=http://127.0.0.1:8080/springdemo/shiro-cas"/>
<property name="successUrl" value="/"/>
<!-- 用户访问未对其授权的资源时,所显示的连接 -->
<property name="unauthorizedUrl" value="/static/no-permission.jsp"/>
<!-- The 'filters' property is usually not necessary unless performing
an override, which we want to do here (make authc point to a PassthruAuthenticationFilter
instead of the default FormAuthenticationFilter: -->
<property name="filters">
<util:map>
<!-- 添加casFilter到shiroFilter整合 -->
<entry key="casFilter" value-ref="casFilter"/>
<!-- <entry key="perms">
<bean
class="com.lora.shiro.filter.MyPermissionsAuthorizationFilter" />
</entry> -->
</util:map>
</property>
<!-- 读取自定义权限内容-->
<property name="filterChainDefinitions">
<value>
/shiro-cas = casFilter
/addUser/** = authc
/show/** = authc
/login = anon
/toLogin/login = anon
/static/** = anon
/static/no-permission.jsp = anon
/greeting** = authc,perms[admin:manage]
/** = anon
<!-- /home* = roles["adminHome"] -->
</value>
</property>
</bean>
<!-- 单点登录下的配置 -->
<bean id="casRealm" class="com.lora.shiro.MyCasRealm">
<property name="defaultRoles" value="ROLE_USER"/>
<!-- cas服务端地址前缀 -->
<property name="casServerUrlPrefix" value="http://cas.uat.qa.nt.ctripcorp.com/caso" />
<!-- 应用服务地址,用来接收cas服务端票据 -->
<!-- 客户端的回调地址设置,必须和上面的shiro-cas过滤器casFilter拦截的地址一致 -->
<property name="casService" value="http://127.0.0.1:8080/springdemo/shiro-cas" />
</bean>
<!-- 这里主要是设置自定义的单Realm应用,若有多个Realm,可使用'realms'属性代替 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<!-- 设置自定义realm ref="iniRealm,mySecurityRealm" -->
<!-- <property name="realm" ref="myRealm"/> -->
<property name="realm" ref="casRealm"/>
</bean>
<!--保证实现了Shiro内部lifecycle函数的bean执行 -->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
<!-- 相当于调用SecurityUtils.setSecurityManager(securityManager) -->
<bean
class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="staticMethod"
value="org.apache.shiro.SecurityUtils.setSecurityManager" />
<property name="arguments" ref="securityManager" />
</bean>
<!-- 用于开启 Shiro Spring AOP 权限注解的支持 -->
<bean
class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager" />
</bean>
</beans>
第四步、 添加自定义realm配置的实现类MyCasRealm
package com.lora.shiro;
import java.util.Iterator;
import java.util.Map;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.cas.CasRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import com.lora.model.User;
public class MyCasRealm extends CasRealm {
/**
* 授权,获取用户的角色、权限
*/
@SuppressWarnings("rawtypes")
@Override
protected AuthorizationInfo doGetAuthorizationInfo(
PrincipalCollection principals) {
if (principals == null) {
throw new AuthorizationException("Principal对象不能为空");
}
Iterator it = principals.fromRealm(getName()).iterator();
String username = null;
if (it.hasNext()) {
username = (String) it.next();
} else {
username = principals.toString();
}
//获取用户响应的permission
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
if (username != null) {
// 添加一个角色,不是配置意义上的添加,而是证明该用户拥有admin角色
info.addRole("admin");
// 添加权限
info.addStringPermission("admin:manage");
System.out.println("已为用户赋予了[admin]角色和[admin:manage]权限");
return info;
}
return info;
}
/**
* 认证,登录验证。
*/
@SuppressWarnings("rawtypes")
@Override
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken token) throws AuthenticationException {
AuthenticationInfo authenticationInfo = super
.doGetAuthenticationInfo(token);
PrincipalCollection principalCollection = authenticationInfo
.getPrincipals();
Map loginDataMap = (Map) principalCollection.asList().get(1);
String userAccount = (String) loginDataMap.get("name");
String userCode = (String) loginDataMap.get("employee");
String fullName = (String) loginDataMap.get("sn");
String department = (String) loginDataMap.get("department");
String email = (String) loginDataMap.get("mail");
User user = new User();
user.setUserCode(userCode);
user.setUserAccount(userAccount);
user.setUname(fullName);
user.setDepartment(department);
user.setEmail(email);
// 将用户保存在SESSION回话中
Session session = SecurityUtils.getSubject().getSession();
session.setAttribute("currentUser", user);
return authenticationInfo;
}
}
以上步骤完成,shiro集成cas的步骤就完成了,实现了单点登录。