关于Shiro的介绍网上有太多,就不赘述了,写这篇文章的目的就是记录一下使用配置的要点。
1. Web.xml配置,Shiro的filter必须放在其他filter之前
<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>
2. Spring Context配置,都是官网摘来的,就不详述了。有以下问题需要注意:
1) Shiro提供的realm没有需要的,所以自己写了个
2) successUrl和unauthorizedUrl调试时曾经没有起作用,后来莫名其妙好了,原因不明,可能是开始代码或配置有问题,比如:为了测试权限注解,在login Action里调用了一个加上了@RequiresPermissions("account:create")的private method,不深究了。
<!-- shiro security --> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <property name="securityManager" ref="securityManager"/> <property name="loginUrl" value="/login"/> <property name="successUrl" value="/welcome"/> <property name="unauthorizedUrl" value="/refuse"/> <property name="filterChainDefinitions"> <value> /refuse = anon <!-- /welcome = perms[accout:edit] --> /** = authc </value> </property> </bean> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <property name="realm" ref="customRealm"/> </bean> <bean id="customRealm" class="com.capgemini.framework.common.access.CustomShiraRealm" /> <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />
3. 为了支持Shiro的注释,按官方文档的介绍,在applicationContext.xml加两个bean定义:DefaultAdvisorAutoProxyCreator和AuthorizationAttributeSourceAdvisor。
可是测试下来却不起作用,搜了半天,终于找到原因,原来用了spring mvc的话就需要把这两个bean定义写在相应的springmvc-servlet.xml文件中,并加上SimpleMappingExceptionResolver
<!-- Support Shiro Annotation --> <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"> <property name="exceptionMappings"> <props> <prop key="org.apache.shiro.authz.UnauthorizedException">shiro-test/refuse</prop> </props> </property> </bean> <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor"/> <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"> <property name="securityManager" ref="securityManager"/> </bean>
4. 自定义的realm必须继承AuthorizingRealm,并实现两个Abstract方法doGetAuthorizationInfo和doGetAuthenticationInfo
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { String username = (String) principals.fromRealm(getName()).iterator().next(); if (username != null) { try { User user = userMgntService.getUserByUserCode(username); if (user != null && user.getRole() != null) { SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); info.addRole(user.getRole().getRoleName()); info.addStringPermission("account:view"); return info; } } catch (AppException e) { logger.error(e, e); } } return null; }
protected AuthenticationInfo doGetAuthenticationInfo( AuthenticationToken authcToken ) throws AuthenticationException { UsernamePasswordToken token = (UsernamePasswordToken) authcToken; String userName = token.getUsername(); if (userName != null && !"".equals(userName.trim())) { try { User user = userMgntService.getUserByUserCode(token.getUsername()); if (user != null && user.getPassword().equals(String.valueOf(token.getPassword()))) return new SimpleAuthenticationInfo(user.getUserCode(), user.getPassword(), getName()); } catch (AppException e) { logger.error(e, e); } } return null; }
5. 如果jsp的login form按照官方文档的写法,表单元素的名字都和文档一样的话,那么login Action基本不需要做太多操作,只需要完成验证失败的跳转。验证成功的话不会回到/login,而是从realm一直往下走跳到successUrl,默认的FormAuthenticationFilter会找这三个requestparameters:username,password andrememberMe,如果非要用不一样的名字,那么就设置FormAuthenticationFilter的这几个参数
<form action="${pageContext.request.contextPath}/login" method="post"> Username: <input type="text" name="username"/> <br/> Password: <input type="password" name="password"/> ... <input type="checkbox" name="rememberMe" value="true"/>Remember Me? ... </form>
[main] ... authc.loginUrl = /whatever.jsp authc.usernameParam = somethingOtherThanUsername authc.passwordParam = somethingOtherThanPassword authc.rememberMeParam = somethingOtherThanRememberMe ...
@RequestMapping(value = "/login") public String login(String username, String password) { return "shiro-test/login"; }