一、背景

    一直以来对shiro的formAuthenticationFilter的工作流程有疑惑,今天终于明白了。这篇文档不是记录formAuthenticationFilter的内部流程,或者说具体的内部代码是怎么工作的。而是记录了将shiro集成到springboot中时,使用formAuthenticationFilter如何拦截、redirect请求。

二、基础配置

shiro的配置

    @Bean(name\="shiroFilter")  
public ShiroFilterFactoryBean shiroFilter(SecurityManager manager) {  
    ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();  
  shiroFilterFactoryBean.setSecurityManager(manager);  
  //拦截器.  
  Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();  
  // 配置不会被拦截的链接 顺序判断  
  filterChainDefinitionMap.put("/static/\*\*", "anon");  
  //配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了  
  filterChainDefinitionMap.put("/sys/logout", "logout");  
  //如果新增url,在此处增加过滤(但是我不想每新增一个路径就在这里配置一次,所以新增了一个filter,CheckLoginStatusFilter)  
  filterChainDefinitionMap.put("/user","authc");  
  
  
  // 如果不设置默认会自动寻找Web工程根目录下的"/login"页面  
  shiroFilterFactoryBean.setLoginUrl("/sys/login");  
  // 登录成功后要跳转的链接  
  shiroFilterFactoryBean.setSuccessUrl("/sys/success");  
  
  //未授权界面;  
  shiroFilterFactoryBean.setUnauthorizedUrl("/sys/unauthorized");  
  shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);  
 return shiroFilterFactoryBean;  
}

说明

    上面的代码中,设置了loginUrl是/sys/login,设置了successUrl是/sys/success

三、流程

1、用户提交了一个form表单,提交的地址是/sys/login,参数包括username和password。
2、formAuthenticationFilter拦截到了该请求,执行AuthorizingRealm里的登录逻辑。
3、如果登录成功,将会执行redirect到/sys/success。如果登录失败,将会执行redirect到/sys/login。

四、疑惑

1、用户在没有登录的时候,任意提交一个表单,会不会执行登录逻辑。
答案:不会。任意提交的表单,登录地址如果不是/sys/login,首先也会被formAuthenticationFilter拦截到,但是拦截到以后因为校验到没有session,即用户还没有登录,会直接redirect到/sys/login。
2、登录失败后如何处理。
登录失败后,首先会执行formAuthenticationFilter中的onLoginFailure,这个函数是继承下来的,原始的org.apache.shiro.web.filter.authc.FormAuthenticationFilter就有这个函数,我的子类FormAuthenticationFilter是继承了该函数。登录失败后,可以在该函数中根据exception来加入一些中文提示,放到request里,再redirect到/sys/login以后,就可以给出登录错误的原因提示。

五、我的formAuthenticationFilter

public class FormAuthenticationFilter extends org.apache.shiro.web.filter.authc.FormAuthenticationFilter {  
  
    public static final String DEFAULT\_MESSAGE\_PARAM \= "message";  
  
    private String messageParam \= DEFAULT\_MESSAGE\_PARAM;  
  
    private static final Logger logger \= LoggerFactory.getLogger(FormAuthenticationFilter.class);  
  
    @Override  
    protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) {  
  
        String username = getUsername(request);  
  String password = getPassword(request);  
  
  logger.info("------------------username: {}, password: {}", username, password);  
  
 if (password == null) {  
            password = "";  
  }  
        boolean rememberMe = isRememberMe(request);  
  String host = StringUtils.getRemoteAddr((HttpServletRequest) request);  
 return new UsernamePasswordToken(username, password.toCharArray(), rememberMe, host);  
  }  
  
  
    public String getMessageParam() {  
        return messageParam;  
  }  
  
  
  @Override  
  protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {  
        HttpServletRequest req = (HttpServletRequest)request;  
 return super.onAccessDenied(request, response);  
  }  
  
    /\*\*  
 \* 登录失败调用事件  
  \*/  
  @Override  
  protected boolean onLoginFailure(AuthenticationToken token,  
  AuthenticationException e, ServletRequest request, ServletResponse response) {  
  
        String className = e.getClass().getName();  
  String message = "";  
 if (UnknownAccountException.class.getName().equals(className)) {  
            message = "用户名错误, 请输入正确的用户名!";  
  } else if (IncorrectCredentialsException.class.getName().equals(className)) {  
            message = "密码错误, 请输入正确的密码; 如果忘记密码请联系管理员重置您的密码!";  
  } else if (e.getMessage() != null && StringUtils.startsWith(e.getMessage(), "msg:")) {  
            message = StringUtils.replace(e.getMessage(), "msg:", "");  
  } else {  
            message = "系统出现点问题,请稍后再试!";  
  e.printStackTrace(); // 输出到控制台  
  }  
        request.setAttribute(getFailureKeyAttribute(), className);  
  request.setAttribute(getMessageParam(), message);  
 return true;  }  
  
    @Override  
  protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {  
        System.out.println("登陆成功");  
 return super.onLoginSuccess(token, subject, request, response);  
  }  
}

六、浏览器提交数据的方式

1、form提交,可以是post,也可以是get
2、form-data,提交文件
3、json
4、xml
我一直以为form表单提交必须是post提交


SHIYIBODEC
26 声望2 粉丝

引用和评论

0 条评论