参考:
http://kdboy.iteye.com/blog/1103794
http://blog.youkuaiyun.com/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过滤器都是干什么用的?
anon | org.apache.shiro.web.filter.authc.AnonymousFilter |
authc | org.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
|
ssl | org.apache.shiro.web.filter.authz.SslFilter |
user | org.apache.shiro.web.filter.authc.UserFilter |
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是第二组
还有就是自定义过滤器了,举个官方的例子
<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了,这个就不说了。