Shiro整合SSH开发2:结合Struts2实现登陆和退出以及Shiro执行流程和原理解析

时间:2021-11-30 17:53:51
先说明下Shiro的内置过滤器:           anon(匿名)  org.apache.shiro.web.filter.authc.AnonymousFilter      anon:例子/admins/**=anon 没有参数,表示可以匿名使用。


     authc(身份验证)      org.apache.shiro.web.filter.authc.FormAuthenticationFilter      authc:例如/admins/user/**=authc表示需要认证(登录)才能使用,FormAuthenticationFilter是表单认证,没有参数 


     authcBasic(http基本验证)      org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter      authcBasic:例如/admins/user/**=authcBasic没有参数表示httpBasic认证


     logout(退出)      org.apache.shiro.web.filter.authc.LogoutFilter


     noSessionCreation(不创建session)      org.apache.shiro.web.filter.session.NoSessionCreationFilter


     perms(许可验证)      org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter      perms:例子/admins/user/**=perms[user:add:*],参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,例如/admins/user/**=perms["user:add:*,user:modify:*"],当有多个参数时必须每个参数都通过才通过,想当于isPermitedAll()方法。


     port(端口验证)        org.apache.shiro.web.filter.authz.PortFilter      port:例子/admins/user/**=port[8081],当请求的url的端口不是8081是跳转到schemal://serverName:8081?queryString,其中schmal是协议http或https等,serverName是你访问的host,8081是url配置里port的端口,queryString      是你访问的url里的?后面的参数。


     rest  (rest方面)       org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter      rest:例子/admins/user/**=rest[user],根据请求的方法,相当于/admins/user/**=perms[user:method] ,其中method为post,get,delete等。


     roles(权限验证)       org.apache.shiro.web.filter.authz.RolesAuthorizationFilter      roles:例子/admins/user/**=roles[admin],【表示必须具有admin参数才可以访问】参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,当有多个参数时,例如admins/user/**=roles["admin,guest"],每个参数通过才算通过,相当于hasAllRoles()方法。


     ssl (ssl方面)        org.apache.shiro.web.filter.authz.SslFilter      ssl:例子/admins/user/**=ssl没有参数,表示安全的url请求,协议为https


     user (用户方面)       org.apache.shiro.web.filter.authc.UserFilter      user:例如/admins/user/**=user没有参数表示必须存在用户, 身份认证通过或通过记住我认证通过的可以访问,当登入操作时不做检查


注: anon,authcBasic,auchc,user是认证过滤器, perms,roles,ssl,rest,port是授权过滤器 ...




此文老猫原创,转载请加本文连接:http://blog.csdn.net/nthack5730/article/details/51124171

更多有关老猫的文章:http://blog.csdn.net/nthack5730



关于Shiro基础整合的applicationContext-shiro.xml配置在之前的文章有说明http://blog.csdn.net/nthack5730/article/details/51002218,这里就不详说了,注意application-shiro.xml里面的loginUrl的配置和下面提到的Action的请求地址需要一致
设置静态资源的匿名访问: Edit<property name="filterChainDefinitions">
    <value>
        <!-- 对静态资源设置匿名访问 -->
        /js/** = anon
        /css/**  = anon
        /img/**  = anon
        /fonts/**  = anon
        /scripts/**  = anon  
    </value>
</property>




登陆:      1.使用FormAuthenticationFilter过滤器实现, 原理如下:      当用户没有认证时,请求loginurl进行认证【在applicationContext-shiro.xml 配置中】,用户身份和用户密码提交数据到loginurl      从表单提交认证的request中被FormAuthenticationFilter拦截住,取出request中的username和password【这两个参数的名称是可以配置的】。      FormAuthenticationFilter调用realm传入一个token(username和password【从request中提取的】)      realm认证时,根据username查询用户的信息【包括usercode】      如果查询不到,realm返回null,FormAuthenticationFilter向request域中填充参数(记录了异常信息)


     2.修改页面,表单提交申请为post      由于FormAuthenticationFilter的取的是request中的username和password,所以        需要修改表单中的“登陆用户”和“密码”的input的name为"username"和"password"      我就出了一个错误:一定要用【post】,shiro才能拦截你的请求,否则你的请求会被跳过而让struts2来处理!      【我用get是为了看URL接口数据的,没想到这个弱智的问题让我对着shiro调了半天!在FormAuthenticationFilter 源码中可以查找到,FormAuthenticationFilter 需要是POST方法才会执行认证操作】 Edit/**
 * This default implementation merely returns <code>true</code> if the request is an HTTP <code>POST</code>,
 * <code>false</code> otherwise. Can be overridden by subclasses for custom login submission detection behavior.
 *
 * @param request  the incoming ServletRequest
 * @param response the outgoing ServletResponse.
 * @return <code>true</code> if the request is an HTTP <code>POST</code>, <code>false</code> otherwise.
 */

@SuppressWarnings({"UnusedDeclaration"})
protected boolean isLoginSubmission(ServletRequest request, ServletResponse response) {
    return (request instanceof HttpServletRequest) && WebUtils.toHttp(request).getMethod().equalsIgnoreCase(POST_METHOD);
}

此文老猫原创,转载请加本文连接:http://blog.csdn.net/nthack5730/article/details/51124171

更多有关老猫的文章:http://blog.csdn.net/nthack5730



     下面po出登陆页面的表单代码:【有h5基础的可以自行化简,action可以留空,表示提交到本页面(bean文件中loginUrl里面的地址)】
<form action="${pageContext.request.contextPath }/login.action" method="post">
    <div>
        <input type="text" name="username" class="username"
            placeholder="用户名 / UID" autocomplete="off" required/>
    </div>
    <div>
        <input type="password" name="password" class="password"
            placeholder="密码" oncontextmenu="return false"
            onpaste="return false" required/>
    </div>
    <button id="submit" type="submit" class="btn btn-success btn-block loginbtn">登陆</button>
</form>



     3.配置Action以及对应的struts配置文件      Shiro的功能是可以对在applicationContext-shiro.xml中的loginUrl里面配置好的action地址进行自动拦截登陆表单发送过来的request并取出在request中的username和password进行验证【也就是说Action的登陆请求是和Shiro关联的,Shiro在登陆认证的时候也只是对这个loginUrl进行拦截(如果有更加高级的用法以后学到就会给大家进一步的说明)】。      Shiro将拦截到的username和password交给realm进行自定义的认证和授权,并且将授权的信息交给shiro的缓存管理器进行管理。
     Shiro源码在FormAuthenticationFilter 中创建token方法: Editprotected AuthenticationToken createToken(ServletRequest request, ServletResponse response) {
    String username = getUsername(request);
    String password = getPassword(request);
    return createToken(username, password, request, response);
}
     上面的createToken调用了另一个抽象类里面的方法,返回一个AuthenticationToken。。。。blahblahblah,最后扔给realm处理。
     登陆的Action是为了让服务器跳转到登陆的页面让用户登陆(如果是首页即要登陆可以不写)。至于Action的写法不变,Action只需要处理好Shiro在登陆失败的时候返回的异常信息即可,以前写好了Action的登陆操作就可以暂时屏蔽掉。也就是说,如果不需要处理异常信息,可以使用动态方法调用,连Action都省掉了。不过为了登陆反馈信息全面,还是建议大家写的。下面会给大家一步步说明【暂时不说动态方法调用】。      其实说那么多就是想把我自己对Shiro和SSH整合后认证和授权的流程的理解用自己的话语给大家理顺一遍,我也是新手,所以我觉得有必要在学习的过程中进行一步步的总结。希望和大家一起进步。
     好了,废话不多说,下面给出我登陆的Action代码:【简化版的Action,不对返回信息作任何处理的,在下面会给出处理信息版的】 public String login() {
    return  "login";
}
          还需要对Action进行Controller的注解支持:@Controller("LoginAction")
@Scope("prototype")
         好了,下面给出我的Action对应的Struts配置文件:
<!-- user名空间 -->
<package name="user" namespace="/user" extends="struts-default"> <!-- 登陆提交的地址,和applicationContext-shiro.xml中配置的loginurl一致 -->
<action name="login" class="com.my.action.LoginAction" method="login">
<result name="login">/WEB-INF/jsp/login.jsp</result>
</action>
</package>
 
     为什么这样配置呢,其实很简单,在上面也说了原理,在这里再说一次吧(以本项目为例):     当你提交表单到/user/login.action的时候,FormAuthenticationFilter拦截了你提交的请求。     然后FormAuthenticationFilter提取出request中的username和password字段,经过一系列的封装操作封装为Token交给realm。     之后realm根据你自定义的认证查询流程进行认证,并将认证结果返回给FormAuthenticationFilter 。     如果认证失败,FormAuthenticationFilter将认证信息填充到request的“shiroLoginFailure”中;     如果认证成功,FormAuthenticationFilter会让浏览器重新访问上一个路径(关于“上一个路径”上面有解释)
     那么我们需要处理信息的话,Action就这么写:public String login() throws Exception {
    //从request中获取FormAuthenticationFilter填充的异常信息,就是ShiroLoginFailure的全限定名
    String exceptionClassName = (String) request.get("shiroLoginFailure");

    //根据Shiro返回的异常类信息判断,抛出并处理这个异常信息
    if (UnknownAccountException.class.getName().equals(exceptionClassName)) {
        error = "用户不存在,请核对用户名";//如果UnknownAccountException抛出这个异常,表示账号不存在

    } else if (IncorrectCredentialsException.class.getName().equals(
            exceptionClassName)) {
        error = "用户名/密码错误";
    } else if (exceptionClassName != null) {
        error = "其他错误:" + exceptionClassName;
    } 

    //此方法不处理登陆成功,shiro认证成功会跳转到上一个路径 
    //登陆失败,还到login页面
    return "login";
}

     上面主要是通过“shiroLoginFailure ”返回的String字段和异常类的名字作比较,根据不同的异常类做出相应的处理。     UnknownAccountException:表示用户不存在。     IncorrectCredentialsException:表示用户能找到但是密码错误。     好了,登陆需要对应处理的Struts的Action和配置文件代码已经都写完了。如果大家在配置过程中出现什么问题,可能是某些细节方面没有注意好。特别是在配置shiro和spring的bean配置文件中的loginUrl一定要和Struts2里面的登陆页面的Action匹配。
注:我的Action中的request对象是通过BaseAction类实现RequestAware接口来得到的。BaseAction部分代码如下:public class BaseAction<Textends ActionSupport implements RequestAware,
        SessionAwareApplicationAwareModelDriven<T
{
    /**
     * 
     */

    private static final long serialVersionUID = 1L;
    protected T model; // 这里使用protected是为了可以封装 也可以继承

    public Map<String, Object> application;
    public Map<String, Object> request;
    public Map<String, Object> session;
    ...
}

此文老猫原创,转载请加本文连接:http://blog.csdn.net/nthack5730/article/details/51124171

更多有关老猫的文章:http://blog.csdn.net/nthack5730





     4.配置认证拦截过滤器      在applicationContext-shiro.xml中配置:【包括静态资源的匿名访问】 Edit<property name="filterChainDefinitions">
    <value>
        <!-- 对静态资源设置匿名访问 -->
        /js/** = anon
        /css/**  = anon
        /img/**  = anon
        /fonts/**  = anon
        /scripts/**  = anon

        <!-- /** = authc 表示所有URL都必须认证才可以通过访问 -->
        /** = authc 

    </value>
</property>


此文老猫原创,转载请加本文连接:http://blog.csdn.net/nthack5730/article/details/51124171

更多有关老猫的文章:http://blog.csdn.net/nthack5730




退出:      不用我们去实现退出,只要去访问一个退出的url(该url可以不存在),由LogoutFilter拦截住,清除session      在applicationContext-shiro.xml中配置:【在上边加入】 <!-- 请求logout.action地址,shiro去清除session -->
/logout.action = logout