上篇spring集成shiro后续…
spring集成shiro主要是org.apache.shiro.web.filter.authc.FormAuthenticationFilter类。
1、controller
@Controller
public class HelloSsm {
@RequestMapping(value = {"/","tologin.do"})
public String tologin(){
return "models/sys/login";
}
@RequestMapping("login.do")
public String login(Model model, User user, HttpServletRequest request){
//进入login.do说明shiro认证失败
/**
* 在FormAuthenticationFilter中,将认证失败的异常类信息保存在request的shiroLoginFailur中
*/
String className = (String) request.getAttribute("shiroLoginFailure");
if(UnknownAccountException.class.getName().equals(className)){
System.out.println("账号有误!");
}else if(DisabledAccountException.class.getName().equals(className)){
System.out.println("禁用的账号!");
}else if(ExcessiveAttemptsException.class.getName().equals(className)){
System.out.println("登录次数过多!");
}else if(ConcurrentAccessException.class.getName().equals(className)){
System.out.println("并发访问异常!");
}else if(IncorrectCredentialsException.class.getName().equals(className)){
System.out.println("密码错误!");
}else if(ExpiredCredentialsException.class.getName().equals(className)){
System.out.println("密码过期!");
}else {
System.out.println("系统错误");
}
String message = "账号或密码错误,请重试...";
request.setAttribute("message",message);
return "models/sys/login";
}
@RequestMapping("index.do")
public String index(Model model,HttpServletRequest request){
User user = (User) SecurityUtils.getSubject().getPrincipal();
if(user!=null) {
request.setAttribute("username", user.getUsername());
}
return "models/sys/index";
}
}
2、login.jsp
注意:form提交表单需要post方式,否则FormAuthenticationFilter将无法进入自定义realm中的认证方法。
<form action="login.do" style="background-color: burlywood" method="post">
username:<input type="text" name="username"><br/>
password:<input type="password" name="password"><br/>
<c:if test="${message!=null && message!=''}">
<input type="text" value="${message}" style="font-weight: bold;font-size:10px;">
</c:if>
<input type="submit" value="login">
</form>
3、跑起来,你可能会碰到的坑~
(1)无法进入realm的认证方法
查看表单提交方式是否是post
(2)无法注入service
这个问题曾困扰了好久,原因有很多。
首先是web容器中filter加载优先于servlet,为此在filter中无法使用spring注解,需要将spring加载优先级设置更高(当然也可能无效)
其次:仍然无效时需要通过工具类获取spring上下文,以获取需要注入的service
loginService = SpringBeanFactoryUtils.getBean(LoginService.class);
@Component
public class SpringBeanFactoryUtils implements ApplicationContextAware{
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if (SpringBeanFactoryUtils.applicationContext == null) {
SpringBeanFactoryUtils.applicationContext = applicationContext;
}
}
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
//根据名称(@Resource 注解)
public static Object getBean(String name) {
return getApplicationContext().getBean(name);
}
//根据类型(@Autowired)
public static <T> T getBean(Class<T> clazz) {
return getApplicationContext().getBean(clazz);
}
public static <T> T getBean(String name, Class<T> clazz) {
return getApplicationContext().getBean(name, clazz);
}
}
(3)loginUrl和successUrl及filterChainDefinitions配置
注意路径设置与认证不要冲突
(4)认证失败
首先是确保是否获取了完整的用户信息。在realm的认证入参AuthenticationToken中查看
其次是断点跟踪认证是否正常
(5)认证完成跳转路径
认证后跳转路径是根据shiro的xml配置确定的,个人建议最好设置successUrl。这样当认证失败时可完整记录失败异常信息
说一下认证失败时信息获取:
/**
* 在FormAuthenticationFilter中,将认证失败的异常类信息保存在request的shiroLoginFailur中
*/
String className = (String) request.getAttribute("shiroLoginFailure");