Shiro基于Spring的实现

参考:

http://blog.csdn.net/hxpjava1/article/details/7035724

http://blog.itpub.net/23071790/viewspace-709367/

http://www.infoq.com/cn/articles/apache-shiro

org.apache.shiro.spring.web.ShiroFilterFactoryBean DOC

在web.xml中配置Shiro的过滤器

<filter>
	<filter-name>shiroFilter</filter-name>
	<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
	<filter-name>shiroFilter</filter-name>
	<url-pattern>/*</url-pattern>
	<dispatcher>REQUEST</dispatcher>
	<dispatcher>FORWARD</dispatcher>
</filter-mapping>

 可以在<filter></filter>中加入targetFilterLifecycle

<init-param>
	<param-name>targetFilterLifecycle</param-name>
	<param-value>true</param-value>
</init-param>

如果设置"targetFilterLifecycle"为true,则spring来管理Filter.init()和Filter.destroy();若为false,则这两个方法失效。

在Spring的配置文件中添加Shiro的过滤器

<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
	<property name="securityManager" ref="securityManager" />
	<property name="loginUrl" value="/login" />
	<property name="successUrl" value="/wellcome" />
	<property name="unauthorizedUrl" value="/forbid" />
	<property name="filterChainDefinitions">
		<value>
			/login = authc
			/logout = logout
			/account/** = user
			/admin/** = authcBasic
		</value>
	</property>
</bean>

 这个时候就遇到一个问题,Shiro过滤器都是干什么用的?

anonorg.apache.shiro.web.filter.authc.AnonymousFilter
authcorg.apache.shiro.web.filter.authc.FormAuthenticationFilter
authcBasic
org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter
perms
org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter
port
org.apache.shiro.web.filter.authz.PortFilter
rest
org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter
roles
org.apache.shiro.web.filter.authz.RolesAuthorizationFilter
sslorg.apache.shiro.web.filter.authz.SslFilter
userorg.apache.shiro.web.filter.authc.UserFilter
写道
rest:例子/admins/user/**=rest[user],根据请求的方法,相当于/admins/user/**=perms[user:method] ,其中method为post,get,delete等。
port:例子/admins/user/**=port[8081],当请求的url的端口不是8081是跳转到schemal://serverName:8081?queryString,其中schmal是协议http或https等,serverName是你访问的host,8081是url配置里port的端口,queryString
是你访问的url里的?后面的参数。
perms:例子/admins/user/**=perms[user:add:*],perms参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,例如/admins/user/**=perms["user:add:*,user:modify:*"],当有多个参数时必须每个参数都通过才通过,想当于
isPermitedAll()方法。
roles:例子/admins/user/**=roles[admin],参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,当有多个参数时,例如/admins/user/**=roles["admin,guest"],每个参数通过才算通过,相当于hasAllRoles()方法。
anon:例子/admins/**=anon 没有参数,表示可以匿名使用。
authc:例如/admins/user/**=authc表示需要认证才能使用,没有参数
authcBasic:例如/admins/user/**=authcBasic没有参数表示httpBasic认证
ssl:例子/admins/user/**=ssl没有参数,表示安全的url请求,协议为https
user:例如/admins/user/**=user没有参数表示必须存在用户,当登入操作时不做检查

这些过滤器分为两组,一组是认证过滤器,一组是授权过滤器。其中anon,authcBasic,auchc,user是第一组,
perms,roles,ssl,rest,port是第二组
authcBasic就是弹出一个登录窗口,要求用户输入用户名和密码。适合于对安全性要求不高的系统或设备中,如大家所用路由器的配置页面的认证

还有就是自定义过滤器了,举个官方的例子

<bean id="myCustomFilter" class="com.class.that.implements.javax.servlet.Filter"/>
 <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
    <property name="filterChainDefinitions">
        <value>
            /some/path/** = authc, myCustomFilter
        </value>
    </property>
 </bean>

  

在Spring的配置文件中添加securityManager和realm,加了个ehcache

<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
	<property name="realm" ref="shiroDbRealm" />
	<property name="cacheManager" ref="shiroEhcacheManager" />
</bean>
<bean id="shiroDbRealm" class="com.**.ShiroDbRealm">
	<property name="accountService" ref="accountService"/>
</bean>
<bean id="shiroEhcacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
	<property name="cacheManagerConfigFile" value="classpath:security/ehcache-shiro.xml"/>
</bean>

ehcache的配置

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xml>
<ehcache updateCheck="false" name="shiroCache">
	<!-- http://ehcache.org/ehcache.xml -->
    <defaultCache
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            overflowToDisk="false"
            diskPersistent="false"
            diskExpiryThreadIntervalSeconds="120"
            />
</ehcache>

又加上了AOP的权限控制和生命周期的配置

<!-- 保证实现了Shiro内部lifecycle函数的bean执行 -->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>

<!-- AOP式方法级权限检查  -->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor">
	<property name="proxyTargetClass" value="true" />
</bean>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
   	<property name="securityManager" ref="securityManager"/>
</bean>

以上已经将配置的东西都弄完了,好像很简单的样子

配置完成就是Java的一些实现了,先说Realm

public class ShiroDbRealm extends AuthorizingRealm {

	protected AccountService accountService;

	/**
	 * 认证回调函数,登录时调用.
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {
		UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
		User user = accountService.findUserByLoginId(token.getUsername());
		if (user != null) {
			if ("disabled".equals(user.getStatus())) {
				throw new DisabledAccountException();
			}

			byte[] salt = Encodes.decodeHex(user.getSalt());
			return new SimpleAuthenticationInfo(new ShiroUser(user.getLoginId(), user.getName()), user.getPassword(),
					ByteSource.Util.bytes(salt), getName());
		} else {
			return null;
		}
	}

	/**
	 * 授权查询回调函数, 进行鉴权但缓存中无用户的授权信息时调用.
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		ShiroUser shiroUser = (ShiroUser) principals.getPrimaryPrincipal();
		User user = accountService.findUserByLoginId(shiroUser.loginName);
		if (user != null) {
			Hibernates.initLazyProperty(user.getRoles());
		}
		SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
		for (Role role : user.getRoles()) {
			// 基于Role的权限信息
			info.addRole(role.getName());
			// 基于Permission的权限信息
			info.addStringPermissions(role.getPermissionList());
		}
		return info;
	}

	/**
	 * 设定Password校验的Hash算法与迭代次数.
	 */
	@PostConstruct
	public void initCredentialsMatcher() {
		HashedCredentialsMatcher matcher = new HashedCredentialsMatcher(AccountService.HASH_ALGORITHM);
		matcher.setHashIterations(AccountService.HASH_INTERATIONS);

		setCredentialsMatcher(matcher);
	}

	public void setAccountService(AccountService accountService) {
		this.accountService = accountService;
	}
}

 自定义了一个ShiroUser,目的是为了保存更多的用户信息

/**
 * 自定义Authentication对象,使得Subject除了携带用户的登录名外还可以携带更多信息.
 */
public static class ShiroUser implements Serializable {
	private static final long serialVersionUID = -1373760761780840081L;
	public String loginName;
	public String name;

	public ShiroUser(String loginName, String name) {
		this.loginName = loginName;
		this.name = name;
	}

	public String getName() {
		return name;
	}

	/**
	 * 本函数输出将作为默认的<shiro:principal/>输出.
	 */
	@Override
	public String toString() {
		return loginName;
	}

	/**
	 * 重载hashCode,只计算loginName;
	 */
	@Override
	public int hashCode() {
		return Objects.hashCode(loginName);
	}

	/**
	 * 重载equals,只计算loginName;
	 */
	@Override
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		if (obj == null) {
			return false;
		}
		if (getClass() != obj.getClass()) {
			return false;
		}
		ShiroUser other = (ShiroUser) obj;
		if (loginName == null) {
			if (other.loginName != null) {
				return false;
			}
		} else if (!loginName.equals(other.loginName)) {
			return false;
		}
		return true;
	}
}

 建了两个PO,user,role

public class User implements Serializable {

	private Long id;
	private String loginId;
	private String password;
	private String salt;
	private String name;
	private String email;
	private Integer status;
	private List<Role> roles = Lists.newArrayList();

	... get/set...
}
public class Role implements Serializable {

	private Long id;
	private String name;
	private String permissions;

	...get/set...

	public List<String> getPermissionList() {
		return ImmutableList.copyOf(StringUtils.split(permissions, ","));
	}
}

 OK。其他的就是登录页面和Controller了,这个就不说了。

相关推荐