zoukankan      html  css  js  c++  java
  • 【Shiro】Apache Shiro架构之集成web

    Shiro系列文章: 
    【Shiro】Apache Shiro架构之身份认证(Authentication) 
    【Shiro】Apache Shiro架构之权限认证(Authorization) 
    【Shiro】Apache Shiro架构之自定义realm 
    【Shiro】Apache Shiro架构之实际运用(整合到Spring中)

     前面两节内容介绍了Shiro中是如何进行身份和权限的认证,但是只是单纯的进行Shiro的验证,简单一点的话,用的是.ini配置文件,也举了个使用jdbc realm的例子,这篇博文主要来总结一下Shiro是如何集成web的,即如何用在web工程中。

    写在前面:本文没有使用web框架,比如springmvc或者struts2,用的是原始的servlet,使用的是.ini配置文件,旨在简单粗暴,说明问题。后面会写一些和框架整合的博文。 
    本文部分参考Apache Shiro的官方文档,文档地址:http://shiro.apache.org/web.html

    新建一个基于maven的web工程,整个工程结构目录如下: 
    工程目录 
      下面来总结一下Shiro集成web的步骤。

    1. 配置

    1.1 web.xml配置

      Shiro如果想要集成到web中,首先需要在web.xml中配置Shiro的监听器和过滤器,如下:

     1 <!-- 添加shiro支持 -->
     2  <listener>
     3    <listener-class>org.apache.shiro.web.env.EnvironmentLoaderListener</listener-class>
     4  </listener>
     5 
     6  <filter>
     7    <filter-name>ShiroFilter</filter-name>
     8    <filter-class>org.apache.shiro.web.servlet.ShiroFilter</filter-class>
     9    <init-param>
    10     <param-name>configPath</param-name>
    11     <param-value>/WEB-INF/shiro.ini</param-value>
    12    </init-param>
    13  </filter>
    14 
    15  <filter-mapping>
    16    <filter-name>ShiroFilter</filter-name>
    17    <url-pattern>/*</url-pattern>
    18  </filter-mapping>

    由上面的配置可以看出,定义Shiro配置文件的路径在/WEB-INF/shiro.ini文件,Shiro的过滤器将会拦截所有的请求。web.xml中其他配置就是servlet的映射配置了,不同的servlet对应不同的请求url,如下:

     1 <servlet>
     2    <servlet-name>LoginServlet</servlet-name>
     3    <servlet-class>demo.shiro.servlet.LoginServlet</servlet-class>
     4  </servlet>  
     5  <servlet-mapping>
     6   <servlet-name>LoginServlet</servlet-name>
     7   <url-pattern>/login</url-pattern>
     8 </servlet-mapping>
     9 
    10  <servlet>
    11    <servlet-name>AdminServlet</servlet-name>
    12    <servlet-class>demo.shiro.servlet.AdminServlet</servlet-class>
    13  </servlet>
    14  <servlet-mapping>
    15   <servlet-name>AdminServlet</servlet-name>
    16   <url-pattern>/admin</url-pattern>
    17 </servlet-mapping>
    18 
    19 <servlet>
    20   <servlet-name>TeacherRoleServlet</servlet-name>
    21   <servlet-class>demo.shiro.servlet.TeacherRoleServlet</servlet-class>
    22 </servlet>
    23 <servlet-mapping>
    24   <servlet-name>TeacherRoleServlet</servlet-name>
    25   <url-pattern>/student</url-pattern>
    26 </servlet-mapping>
    27 <servlet-mapping>
    28 
    29 <servlet>
    30   <servlet-name>LogoutServlet</servlet-name>
    31   <servlet-class>demo.shiro.servlet.LogoutServlet</servlet-class>
    32 </servlet>
    33 <servlet-name>LogoutServlet</servlet-name>
    34   <url-pattern>/logout</url-pattern>
    35 </servlet-mapping>
    36 
    37 <servlet>
    38   <servlet-name>TeacherPermsServlet</servlet-name>
    39   <servlet-class>demo.shiro.servlet.TeacherPermsServlet</servlet-class>
    40 </servlet>
    41 <servlet-mapping>
    42   <servlet-name>TeacherPermsServlet</servlet-name>
    43   <url-pattern>/teacher</url-pattern>
    44 </servlet-mapping>

    1.2 pom.xml配置

      pom文件中需要引入相关的jar

     1 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     2   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
     3   <modelVersion>4.0.0</modelVersion>
     4   <groupId>demo.shiro</groupId>
     5   <artifactId>ShiroWeb</artifactId>
     6   <packaging>war</packaging>
     7   <version>0.0.1-SNAPSHOT</version>
     8 
     9   <dependencies>
    10 
    11     <!-- shiro核心包 -->
    12     <dependency>
    13         <groupId>org.apache.shiro</groupId>
    14         <artifactId>shiro-core</artifactId>
    15         <version>1.2.5</version>
    16     </dependency>
    17     <!-- 添加shiro web支持 -->
    18     <dependency>
    19         <groupId>org.apache.shiro</groupId>
    20         <artifactId>shiro-web</artifactId>
    21         <version>1.2.5</version>
    22     </dependency>
    23 
    24     <!-- 添加sevlet支持 -->
    25     <dependency>
    26         <groupId>javax.servlet</groupId>
    27         <artifactId>javax.servlet-api</artifactId>
    28         <version>3.1.0</version>
    29     </dependency>
    30     <!-- 添加jsp支持 -->
    31     <dependency>
    32         <groupId>javax.servlet.jsp</groupId>
    33         <artifactId>javax.servlet.jsp-api</artifactId>
    34         <version>2.3.1</version>
    35     </dependency>
    36     <!-- 添加jstl支持 -->
    37     <dependency>
    38         <groupId>javax.servlet</groupId>
    39         <artifactId>jstl</artifactId>
    40         <version>1.2</version>
    41     </dependency>
    42     <!-- 添加log4j日志 -->
    43     <dependency>
    44         <groupId>log4j</groupId>
    45         <artifactId>log4j</artifactId>
    46         <version>1.2.17</version>
    47     </dependency>
    48     <dependency>
    49         <groupId>commons-logging</groupId>
    50         <artifactId>commons-logging</artifactId>
    51         <version>1.2</version>
    52     </dependency>
    53     <dependency>
    54         <groupId>org.slf4j</groupId>
    55         <artifactId>slf4j-api</artifactId>
    56         <version>1.7.21</version>
    57     </dependency>
    58     <dependency>
    59       <groupId>junit</groupId>
    60       <artifactId>junit</artifactId>
    61       <version>4.12</version>
    62       <scope>test</scope>
    63     </dependency>
    64   </dependencies>
    65   <build>
    66     <finalName>ShiroWeb</finalName>
    67   </build>
    68 </project>

    2. Shiro的内置过滤器(Default Filters)

      当运行Web应用程序时,Shiro将创建一些有用的内置过滤器实例,并且自动的在[main]部分使用。我们可以人为配置它们,就好比spring中的bean一样,并且在自己定义的url中引用它们。Shiro中内置的过滤器有以下几个:

    过滤器名对应的类
    anon(匿名) org.apache.shiro.web.filter.authc.AnonymousFilter
    authc(身份验证) org.apache.shiro.web.filter.authc.FormAuthenticationFilter
    authcBasic(http基本验证) org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter
    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
    port(端口验证) org.apache.shiro.web.filter.authz.PortFilter
    rest(rest方面) org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter
    roles(权限验证) org.apache.shiro.web.filter.authz.RolesAuthorizationFilter
    ssl(ssl方面) org.apache.shiro.web.filter.authz.SslFilter
    user(用户方面) org.apache.shiro.web.filter.authc.UserFilter

     这些内置的过滤器使用方法是这样的,一般先指定一个请求url对应一个过滤器名,由于该过滤器对应一个类,而这个类中会有一个属性,这个属性是当验证失败的时候指定要跳转到那个url的,所以将这个属性配置成验证失败后你要请求的url即可。这里我举其中几个(匿名,身份验证,许可验证,权限验证)来说明如何使用,其他的类似。

    3. Shiro的配置文件

      针对上面我提到的几个验证,先把shiro.ini文件写好:

     1 [main]
     2 #定义身份认证失败后的请求url映射,loginUrl是身份认证过滤器中的一个属性
     3 authc.loginUrl=/login
     4 #定义角色认证失败后的请求url映射,unauthorizedUrl是角色认证过滤器中的一个属性
     5 roles.unauthorizedUrl=/unauthorized.jsp
     6 #定义角色认证失败后请求url映射,unauthorizedUrl是角色认证过滤器中的一个属性
     7 perms.unauthorizedUrl=/unauthorized.jsp
     8 
     9 #定义几个用户和角色
    10 [users]
    11 csdn1=123,admin,teacher
    12 csdn2=123,teacher
    13 csdn3=123,student
    14 csdn4=123
    15 
    16 #定义不同角色的权限
    17 [roles]
    18 admin=user:*,student:*
    19 teacher=student:*
    20 
    21 #定义请求的地址需要做什么验证
    22 [urls]
    23 #请求login的时候不需要权限,游客身份即可(anon)
    24 /login=anon 
    25 #请求/admin的时候,需要身份认证(authc)
    26 /admin=authc 
    27 #请求/student的时候,需要角色认证,必须是拥有teacher角色的用户才行
    28 /student=roles[teacher]
    29 #请求/teacher的时候,需要权限认证,必须是拥有user:create权限的角色的用户才行
    30 /teacher=perms["user:create"]

      我简单说明一下这个配置,比如[main]中定义的authc.loginUrl=/login,authc是一个内置过滤器,它对应org.apache.shiro.web.filter.authc.AnonymousFilter类,而这个类中有个loginUrl属性,表示验证失败后将要请求的url映射,这个url映射与一个具体的servlet对应,读到这里,可以和上面的servlet配置对比一下就知道了。roles.unauthorizedUrl也是一样的道理,只不过这里验证失败直接请求一个具体的jsp页面而已。 
      [users]中是Subject认证主体,就不再赘述了,[urls]中定义需要拦截哪些请求,并做什么验证。比如/login的url映射请求不拦截,因为是登陆的,请求/admin的url映射的话做身份认证,请求/student的url映射的话要做角色认证,且必须是teacher角色才行,请求/teacher的url映射的话需要进行权限认证,必须有user:create权限的用户才可以通过验证。

    4. 测试程序

      接下来一个个测试上面的这些认证。

    4.1 测试authc身份认证

      测试authc,得有/admin请求,加上上面的servlet配置可知,首先完成LoginServlet和AdminServlet。

    //LoginServlet.java
    public class LoginServlet extends HttpServlet {
    
        public void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            System.out.println("login doGet");
            String username = request.getParameter("username");
            String password = request.getParameter("password");
    
            Subject currentUser = SecurityUtils.getSubject();
            UsernamePasswordToken token = new UsernamePasswordToken(username, password);
            try {
                currentUser.login(token);
                System.out.println("认证成功");
                request.getSession().setAttribute("username", username);
                request.getRequestDispatcher("/success.jsp").forward(request, response);
            } catch (AuthenticationException e) {
                e.printStackTrace();
                System.out.println("认证失败");
                request.getRequestDispatcher("/login.jsp").forward(request, response);
            }
        }
    
        public void doPost(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
    
            doGet(request, response);
        }
    }

    认证成功跳转到success.jsp页面,认证失败的话就进入登录页面,让用户先登录,看一下success.jsp和login.jsp

    <form action="${pageContext.request.contextPath }/login" method="post">
        username:<input type="text" name="username"/><br>
        password:<input type="password" name="password"/><br>
        <input type="submit" value="登陆">
    </form>

    因为登录提交请求的是/login,而/login=anon表示匿名验证,所以不会拦截认证,直接进入LoginServlet,执行我们自己写的认证代码。下面是success.jsp,很简单。

    <body>
         欢迎你${username }
         <a href="${pageContext.request.contextPath }/logout">退出</a>
    </body>

    顺带把LogoutServlet写了:

    public class LogoutServlet extends HttpServlet {
    
        public void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            request.getSession().invalidate(); //直接让session失效
        }
    
        public void doPost(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
    
            doGet(request, response);
        }
    
    }

     然后就是/admin请求了,请求的是AdminServlet.jsp,如果认证成功就会进入该servlet:

    public class AdminServlet extends HttpServlet {
    
        private static final long serialVersionUID = 1L;
    
        public void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            System.out.println("admin doGet");
            request.getRequestDispatcher("/success.jsp").forward(request, response);
        }
    
        public void doPost(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
    
            doGet(request, response);
        }
    
    }

      写好了之后,开启tomcat,在浏览器中访问:http://localhost:8080/ShiroWeb/admin来请求AdminServlet.Java,Shiro根据配置(/admin=authc)会去进行身份认证,但是此时肯定是认证失败,所以根据配置(authc.loginUrl=/login)去请求LoginServlet.java,Shiro不会去验证,因为/login=anon,但是LoginServlet.java程序中会验证,我们自己写的。因为没有用户名和密码,所以认证失败,会跳转到login.jsp登陆页面,输入用户名和密码后,再次请求/login,此时再次进入LoginServlet.java,认证成功,进入success.jsp页面。 
      因为Shiro中会默认有30分钟缓存时间,所以此时我们再次去访问http://localhost:8080/ShiroWeb/admin的时候,就可以访问到AdminServlet.java中了,可以在里面打断点验证,然后掉转到success.jsp。这就是身份认证的过程。

    4.2 测试roles角色认证

      根据配置文件,我们需要请求/student才能触发roles角色认证,所以根据servlet的映射,我们完成TeacherRoleServlet.java代码。

    public class TeacherRoleServlet extends HttpServlet {
    
        public void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            System.out.println("role deget");
            Subject currentUser = SecurityUtils.getSubject();
            //其实是不用判断了,因为只要进来了,肯定角色是对的,否则进不来
            //判断当前用户是否具有teacher角色
            if(currentUser.hasRole("teacher")) {
                request.getRequestDispatcher("/success.jsp").forward(request, response);
            } else {            
                request.getRequestDispatcher("/unauthorized.jsp").forward(request, response);
            }
        }
    
        public void doPost(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
    
            doGet(request, response);
        }
    }

    上面程序的注释中也提到了,其实可以不用再认证了,因为进入该servlet之前,Shiro已经帮我们认证了,只有认证成功才会进入到该servlet,否则会根据配置(roles.unauthorizedUrl=/unauthorized.jsp)会直接跳转到unauthorized.jsp页面显示。

    <body>
         认证未通过,或者权限不足
         <a href="${pageContext.request.contextPath }/logout">退出</a>
    </body>

     然后我们在浏览器中输入http://localhost:8080/ShiroWeb/student来请求TeacherRoleServlet,这里需要注意的是,刚刚测试过后需要点击退出,否则还是当前用户,会影响这次的测试。在测试角色认证的时候,它会先进行身份认证,再进行角色认证。也就是说,Shiro会先跳转到登陆页面让我们登陆,我们可以尝试两个不同的用户csdn1(有teacher角色)和csdn3(没有teacher角色)来测试。

    4.2 测试perms权限认证

      和上面一样,先写TeacherPermsServlet.java:

    public class TeacherPermsServlet extends HttpServlet {
    
        public void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            System.out.println("perms doget");
            Subject currentUser = SecurityUtils.getSubject();
            // 其实是不用判断了,因为只要进来了,肯定角色是对的,否则进不来
            // 判断当前用户是否具有teacher角色
            if (currentUser.isPermitted("user:create")) {
                request.getRequestDispatcher("/success.jsp").forward(request,
                        response);
            } else {
                request.getRequestDispatcher("/unauthorized.jsp").forward(request,
                        response);
            }
        }
    
        public void doPost(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
    
            doGet(request, response);
        }
    }

     然后我们在浏览器中输入http://localhost:8080/ShiroWeb/teacher来请求TeacherPermsServlet ,这里同样需要注意的是,刚刚测试过后需要点击退出,否则还是当前用户,会影响这次的测试。在测试权限认证的时候,它同样会先进行身份认证,再进行权限认证。也就是说,Shiro会先跳转到登陆页面让我们登陆,我们可以尝试两个不同的用户csdn1(有admin角色,从而有user:create权限)和csdn3(没有admin角色,从而没有user:create权限)来测试。

    5. url匹配方式

      这里提一下Shiro在集成web的时候,url可以不用向上面那样写的很死,它可以匹配的,比如:

    /admin?=authc,表示可以请求以admin开头的字符串,如xxx/adminfefe,但无法匹配多个,即xxx/admindf/admin是不行的 
    /admin*=authc表示可以匹配零个或者多个字符,如/admin,/admin1,/admin123,但是不能匹配/admin/abc这种 
    /admin/**=authc表示可以匹配零个或者多个路径,如/admin,/admin/ad/adfdf等。 
    /admin*/**=authc这个就不多说了,结合上面两个就知道了。

    
    
    
  • 相关阅读:

    2018.10.18 常用API部分测试题
    2018.10.18课堂笔记HashMap之前
    JavaScript 的 this 原理
    vue h5转换uni-app
    Vue.js 3.0 新特性预览
    ES6模块与CommonJS的区别
    同源策略和跨域问题
    一口气了解 babel
    媒体查询,移动端常见布局以及其他
  • 原文地址:https://www.cnblogs.com/shanheyongmu/p/5740202.html
Copyright © 2011-2022 走看看