shiro认证授权源码分析

shiro内置了许多过滤器用来控制认证授权

anon : org.apache.shiro.web.filter.authc.AnonymousFilter

authc : org.apache.shiro.web.filter.authc.FormAuthenticationFilter

authcBasic : org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter

perms : org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter

port : org.apache.shiro.web.filter.authz.PortFilter

rest : org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter

roles : org.apache.shiro.web.filter.authz.RolesAuthorizationFilter

ssl : org.apache.shiro.web.filter.authz.SslFilter

user : org.apache.shiro.web.filter.authc.UserFilter


其中认证是否已登录FormAuthenticationFilter

认证是否授权(角色、权限)RolesAuthorizationFilter,PermissionsAuthorizationFilter

(以下以RolesAuthorizationFilter为例

核心都继承自AccessControlFilter

   

左边是认证登录,右边是认证授权。注意红框位置FormAuthenticationFilter 和 RolesAuthorizationFilter 都通过了过渡类继承自AccessControlFilter

简单介绍下这个类的几个方法

onPreHandle:判断认证是否通过,以及后续处理上代码:

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

isAccessAllowed:判断认证是否通过(FormAuthenticationFilter中是认证是否已登录,RolesAuthorizationFilter是认证是否已授权

onAccessDenied:认证失败的后续处理

isLoginRequest:判断是否是登录请求,登录地址在配置文件中已配:

<!--未登录状态下访问authc则进入loginUrl配置的路径-->
 <property name="loginUrl" value="/login"></property>
 <!--登录成功后跳转的默认地址-->
 <property name="successUrl" value="/main"></property>
 <!--如果您请求的资源不再您的权限范围,则跳转到/400请求地址 -->
 <property name="unauthorizedUrl" value="400"></property>

saveRequest:保存当前请求

redirectToLogin:重定向到登录页面

saveRequestAndRedirectToLogin:保存当前请求并重定向到登录页面

认证登录流程按照继承关系:

AccessControlFilter

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

AuthenticatingFilter(判断是否认证通过,将调用父类的isAccessAllowed)

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

AuthenticationFilter(在这里真正判断是否已登录)

protected  boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
   Subject subject = getSubject(request, response);
    return subject.isAuthenticated();
}

登录的话,直接跳转,如果未登录:

FormAuthenticationFilter 

protected  boolean onAccessDenied(ServletRequest request, ServletResponse response)  throws Exception {
    if (isLoginRequest(request, response)) {
        if (isLoginSubmission(request, response)) {
            if (log.isTraceEnabled()) {
               log.trace("Login submission detected.  Attempting to execute login.");
           }
            return executeLogin(request, response);
       }  else {
            if (log.isTraceEnabled()) {
               log.trace("Login page view.");
           }
            //allow them to see the login page ;)
            return  true;
       }
   }  else {
        if (log.isTraceEnabled()) {
           log.trace("Attempting to access a path which requires authentication.  Forwarding to the " +
                   "Authentication url [" + getLoginUrl() + "]");
       }

       saveRequestAndRedirectToLogin(request, response);
        return  false;
   }
}
首先判断是否是登录请求,如果是get形式登录页面跳转继续过滤器链,如果是post表单提交执行executeLogin方法,

将走realm中的doGetAuthenticationInfo方法。

ps:执行executeLogin方法过程中会生成AuthenticationToken,具体代码:

protected AuthenticationToken createToken(String username, String password,
                                         ServletRequest request, ServletResponse response) {
    boolean rememberMe = isRememberMe(request);
   String host = getHost(request);
    return createToken(username, password, rememberMe, host);
}
默认传username,password,所以前台页面name属性要一一对应。

登录成功或失败会走onLoginSuccess或onLoginFailure

如果不是登录请求则保存当前请求跳转至登录页面,如果想封装ajax请求的shiro处理,可自定义自己的FormAuthenticationFilter


认证授权流程按照继承关系

AccessControlFilter

public  boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue)  throws Exception {
        return isAccessAllowed(request, response, mappedValue) || onAccessDenied(request, response, mappedValue);
   }
RolesAuthorizationFilter(认证是否有权限,shiro默认是需要所有角色才认证通过的)

public  boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue)  throws IOException {

      Subject subject = getSubject(request, response);
      String[] rolesArray = (String[]) mappedValue;

       if (rolesArray ==  null || rolesArray.length == 0) {
           //no roles specified, so nothing to check - allow access.
           return  true;
      }

      Set<String> roles = CollectionUtils.asSet(rolesArray);
       return subject.hasAllRoles(roles);
  }

AuthorizationFilter(认证角色未通过后的处理)

protected  boolean onAccessDenied(ServletRequest request, ServletResponse response)  throws IOException {

      Subject subject = getSubject(request, response);
       // If the subject isn't identified, redirect to login URL
       if (subject.getPrincipal() ==  null) {
          saveRequestAndRedirectToLogin(request, response);
      }  else {
           // If subject is known but not authorized, redirect to the unauthorized URL if there is one
           // If no unauthorized URL is specified, just return an unauthorized HTTP status code
          String unauthorizedUrl = getUnauthorizedUrl();
           //SHIRO-142 - ensure that redirect _or_ error code occurs - both cannot happen due to response commit:
           if (StringUtils.hasText(unauthorizedUrl)) {
              WebUtils.issueRedirect(request, response, unauthorizedUrl);
          }  else {
              WebUtils.toHttp(response).sendError(HttpServletResponse.SC_UNAUTHORIZED);
          }
      }
       return  false;
  }

如果未登录,保存当前请求跳转至登录页面,否则判断是否有配置认证权限未通过须要跳转的请求,如果配置了则跳转,否则抛出错误码SC_UNAUTHORIZED(401),可在web.xml中捕获该错误码自己定义要跳转的页面。


这是我理解的shiro认证授权策略,其中认证授权前后所要做的处理都可以通过继承相应类重现方法来达到自定义效果,无需在contrller层写多余的业务逻辑代码,shiro的这些接口和相关配置文件都可以帮你搞定!

最后需要在shiro配置文件中配置

01  < bean  id = "shiroFilter"  class = "org.apache.shiro.spring.web.ShiroFilterFactoryBean" >
02          < property  name = "securityManager"  ref = "securityManager" />
03          <!-- 未登录状态下访问 authc 则进入 loginUrl 配置的路径 -->
04          < property  name = "loginUrl"  value = "/login" ></ property >
05          <!-- 登录成功后跳转的默认地址 -->
06          < property  name = "successUrl"  value = "/main" ></ property >
07           <!-- 如果您请求的资源不再您的权限范围,则跳转到 / 400 请求地址  -->
08          < property  name = "unauthorizedUrl"  value = "400" ></ property >
09          <property name="filters">
10             <map>
11                 <entry key="authc">
12                     <bean class="com.luming.shiro.MyFormAuthenticationFilter"/>
13                 </entry>
14                 <entry key="roles">
15                     <bean class="com.luming.shiro.MyRolesAuthorizationFilter"/>
16                 </entry>
17             </map>
18         </property>

19  </ bean >

MyFormAuthenticationFilter继承自FormAuthenticationFilter

MyRolesAuthorizationFilter继承自RolesAuthorizationFilter

最后基于自身项目的shiro自定义认证授权还须要结合自定义的realm:shiro认证授权

因为JdbcRealm的doGetAuthenticationInfo定义了如何对用户提交的数据进行认证,doGetAuthorizationInfo定义了授予用户何种权限,

FormAuthenticationFilter和RolesAuthorizationFilter主要是负责认证及授权的前后须要做的一些自定义操作,比如IP过滤等

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值