在两天在看Shiro,开涛兄的教程还是写的比较易读,差不多看了一天吧,就准备拿来用了。 可能是想的太简单了,在用的时候确实碰到一些问题,就拿最简单的身份验证来说吧: 需要说明的是,这里是集成在Spring中使用,身份验证我直接使用了Shiro提供的
org.apache.shiro.web.filter.authc.FormAuthenticationFilter
如果url应用了该拦截器,那么处理流程是大致这样的: 由于之前用markdown花的流程图显示不下,所以还是改成图片形式了。
比如我们要用FormAuthenticationFilter来做一个简单的身份验证的话,也是很简单的:
- 首先我们分别写好处理登陆的get请求和post请求
@RequestMapping(value = "/login", method = RequestMethod.GET) public String showLoginPage() { return "user/login"; } @RequestMapping(value = "/login", method = RequestMethod.POST) public String submitLoginForm(User user, HttpServletRequest request, Model model) { String errorClassName = (String) request .getAttribute("shiroLoginFailure"); String authticationError = null; if (UnknownAccountException.class.getName().equals(errorClassName)) { authticationError = "用户名/密码错误"; } else if (IncorrectCredentialsException.class.getName().equals( errorClassName)) { authticationError = "用户名/密码错误"; } else if (errorClassName != null) { authticationError = "未知错误:" + errorClassName; } model.addAttribute("authticationError", authticationError); return showLoginPage(); }
在post请求中我们需要根据request中的错误信息来翻译出需要显示的消息内容。若需要国际化的话,也可以用ResourceBundle来做。 需要注意的是,只有当登陆报错了才会进来这个方法中来。若身份验证成功的话,会直接跳转到之前的访问地址或是successfulUrl去。好了,接下来看看配置文件:
<!-- 凭证匹配器 这里简单写了一个无需加密的匹配 --> <bean id="credentialsMatcher" class="com.zhu.prototype.shiro.credential.PlainPasswordMatcher"> </bean> <bean id="jdbcRealm" class="org.apache.shiro.realm.jdbc.JdbcRealm"> <property name="credentialsMatcher" ref="credentialsMatcher"></property> <property name="authenticationQuery" value="select password from user where username = ?"></property> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 安全管理器 DefaultWebSecurityManager默认使用ServletContainerSessionManager来管理session--> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <property name="realms"> <list> <ref bean="jdbcRealm" /> </list> </property> </bean> <!-- 基于Form表单的身份验证过滤器 --> <bean id="formAuthenticationFilter" class="org.apache.shiro.web.filter.authc.FormAuthenticationFilter"> <property name="usernameParam" value="username" /> <property name="passwordParam" value="password" /> <property name="loginUrl" value="/login" /> <property name="successUrl" value="/news/newsList"></property> </bean>
登陆成功后,会在session中默认设置几个属性:
{org.apache.shiro.subject.support.DefaultSubjectContext_AUTHENTICATED_SESSION_KEY=true, org.apache.shiro.web.session.HttpServletSession.HOST_SESSION_KEY=0:0:0:0:0:0:0:1, org.apache.shiro.subject.support.DefaultSubjectContext_PRINCIPALS_SESSION_KEY=zhu}
如果我们需要注册attribute的话,我的想法是继承FormAuthenticationFilter(不知道Shiro是否有提供别的配置来完成?),然后重写onLoginSuccess(…),原定义如下:
protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception { issueSuccessRedirect(request, response); //we handled the success redirect directly, prevent the chain from continuing: return false; }
可改写为
@Override protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception { super.onLoginSuccess(token, subject, request, response); initCustomSessionAttributes(((HttpServletRequest) request) .getSession(false), token); return false; } protected void initCustomSessionAttributes(HttpSession session, AuthenticationToken token) { String username = token.getPrincipal().toString(); User user = userService.getUserByUsername(username); UserPreferences preferences = new UserPreferences(); preferences.setUsername(user.getUsername()); session.setAttribute("userPreferences", preferences); }
最后给出Shiro中常见的验证不通过报的错误:
- DisabledAccountException (禁用的帐号)
- LockedAccountException (锁定的帐号)
- UnknownAccountException(错误的帐号)
- ExcessiveAttemptsException(登录失败次数过多)
- IncorrectCredentialsException (错误的凭证)
- ExpiredCredentialsException (过期的凭证)
- ……