zno2

shiro 运行流程

单词词性解释
authentication n. 认证
authentic adj. 真实的
authorization n. 授权
authorise vt. 授权

authentication 你是谁

authorization 你能做什么

 

认证流程(可以在controller中进行,也可以在filter中进行):

JdbcRealm.doGetAuthenticationInfo(AuthenticationToken) line: 46 由抽象类最终调用最外层用户自定义方法(比较入参token 和返回info, 身份和凭证都匹配则通过,需要用到credentialsMatcher)
JdbcRealm(AuthenticatingRealm).getAuthenticationInfo(AuthenticationToken) line: 571  
ModularRealmAuthenticator.doSingleRealmAuthentication(Realm, AuthenticationToken) line: 180  
ModularRealmAuthenticator.doAuthenticate(AuthenticationToken) line: 267  
ModularRealmAuthenticator(AbstractAuthenticator).authenticate(AuthenticationToken) line: 198  
DefaultWebSecurityManager(AuthenticatingSecurityManager).authenticate(AuthenticationToken) line: 106 调用父类login
DefaultWebSecurityManager(DefaultSecurityManager).login(Subject, AuthenticationToken) line: 274 调用父类 login
WebDelegatingSubject(DelegatingSubject).login(AuthenticationToken) line: 260 调用 securityManager 的 login 方法 (已配置到xml中)
SystemController.logining(String, String, String) line: 66 spring 调度到该controller , 从这开始,调用subject.login 传入认证token

ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 193

这里循环所有filter (此刻是 shiroFilter 配置在spring bean configuration )

 

 

1 入口是  ShiroFilter

2 穿针引线是靠 ThreadLocal 

3 先走filter ,比如:authc , perms 

4 再走controller ,进行login(token) 

5 login 过程中需要用到 securityManager 、 realm 

 

实例:AdviceFilter

    public void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain)
            throws ServletException, IOException {
            ...
            boolean continueChain = preHandle(request, response);
            ...
            if (continueChain) {
                executeChain(request, response, chain);
            }
            ...
            postHandle(request, response);
            ...
    }

 

chain中是自己配置的过滤器

 

 

 主要是在preHandle 中完成的操作,返回true 则进行下一项过滤

    protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
        ...
        for (String path : this.appliedPaths.keySet()) {
            // If the path does match, then pass on to the subclass implementation for specific checks
            //(first match 'wins'):
            if (pathsMatch(path, request)) {
                ...return isFilterChainContinued(request, response, path, config);
            }
        }

        //no path matched, allow the request to go through:
        return true;
    }

循环配置的所有路径,找到与当前请求路径匹配的路径,

 

 

 

    private boolean isFilterChainContinued(ServletRequest request, ServletResponse response,
                                           String path, Object pathConfig) throws Exception {

        if (isEnabled(request, response, path, pathConfig)) { //isEnabled check added in 1.2
        ...
            //The filter is enabled for this specific request, so delegate to subclass implementations
            //so they can decide if the request should continue through the chain or not:
            return onPreHandle(request, response, pathConfig);
        }
        ...
        //This filter is disabled for this specific request,
        //return 'true' immediately to indicate that the filter will not process the request
        //and let the request/response to continue through the filter chain:
        return true;
    }

当前请求是

((HttpServletRequest)request).getRequestURL()
     (java.lang.StringBuffer) http://localhost:8888/sdfs
((HttpServletRequest)request).getRequestURI()
     (java.lang.String) /sdfs

 

当前请求是/sdfs ,匹配到了 /**

 

 

  如果开启了过滤器,则触发 onPreHandle ,命名方式类似onclick

    public boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
        return isAccessAllowed(request, response, mappedValue) || onAccessDenied(request, response, mappedValue);
    }

触发后,第一步判断就是 isAccessAllowed , 如果被拒绝,则会触发 onAccessDenied (这里是逻辑或运算)

    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        return super.isAccessAllowed(request, response, mappedValue) ||
                (!isLoginRequest(request, response) && isPermissive(mappedValue));
    }

这里super调用是要满足所有父类的逻辑

MyFilter -> AuthenticatingFilter -> AuthenticationFilter

cn.zno.xxx.MyFilter
org.apache.shiro.web.filter.authc.AuthenticatingFilter
org.apache.shiro.web.filter.authc.AuthenticationFilter

也就是说要依次执行下面的逻辑

	1)判断是否已认证通过
        Subject subject = getSubject(request, response);
        subject.isAuthenticated();

	2)判断是否不是登录路径 && 被允许
       (!isLoginRequest(request, response) && isPermissive(mappedValue));

	3) 判断自定义逻辑

 

有一项被拒绝,则会触发 onAccessDenied

    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        if (isLoginRequest(request, response)) {
            if (isLoginSubmission(request, response)) {
                return executeLogin(request, response);
            } else {
                //allow them to see the login page ;)
                return true;
            }
        } else {
            saveRequestAndRedirectToLogin(request, response);
            return false;
        }
    }

目前只有一个onAccessDenied 默认实现

首先判断当前请求地址是否是登录路径/login.html

如果是登录路径且是post提交表单,则执行认证逻辑

如果是登录路径但不是post提交表单,则穿过过滤器,到达controller,展示登录表单

如果不是登录路径则保存当前请求,然后重定向到登录表单页面 (这里不再 executeChain)

 

TIPS:

isAccessAllowed  和  onAccessDenied  的返回值是boolean ,返回false 代表被拦截(可以做重定向),返回true代表放行(可能到达controller)

方法内能干的3件事:

1)放行到达controller 

2)登录认证

3)拦截后重定向

 

// 认证过滤器中都能干什么事情:
// 1) 判断当前请求是否是loginUrl (isLoginRequest
// 2)如果当前请求时loginUrl,判断是否是post 登录表单  (isLoginSubmission
// 3) 执行登录认证 (executeLogin
// 4) 重定向到登录页面 (redirectToLogin
// 5) 重定向到百度页面( WebUtils.issueRedirect(req, res, "http://baidu.com", null, false);

 

TIPS:

什么是 mappedValue 

mappedValue 是 配置的过滤器的 optional_config ,是spring数组,是在ShiroFilter 初始化时解析好的。

通过 String[] DefaultFilterChainManager.splitChainDefinition(String chainDefinition) 解析

回忆一下shiro过滤器语法 

filter1[optional_config1], filter2[optional_config2], ..., filterN[optional_configN]

 

 

 

 转成name + config

而 config 通过 shiro 自己的 StringUtils 的 split 方法转为数组,如果空就是null

而 config 转成的String[] 就是 mappedValues

    protected boolean isPermissive(Object mappedValue) {
        if(mappedValue != null) {
            String[] values = (String[]) mappedValue;
            return Arrays.binarySearch(values, PERMISSIVE) >= 0;
        }
        return false;
    }

这个方法就是要看config中是否包含 "permissive" 

 

posted on 2016-08-05 17:43  zno2  阅读(103)  评论(0编辑  收藏  举报

导航