Object's Blog

关于Shiro框架的学习(二)

字数统计: 4.1k阅读时长: 20 min
2019/08/21 分享

前言

接上篇,关于Shiro框架的学习(一),这篇会记录下Shiro整合Web、整合SSM的过程,之后就可以直接应用在项目的安全控制上。

关于整合Web

  • 环境

    Eclipse、MySQL、Tomcat8

  • 准备工作

    • 创建Dynamic Web工程:
      Dynamic工程
      • 使用到的类
        准备User实体类、ShiroDao类、DatabaseRealm类,这三个类在上一篇文章中已经提及,这里不再重复赘述。
      • 数据库
        沿用上一篇博文中未加密的数据库,数据库脚本上一篇已提及,同样不再重复贴代码了。
      • jar包:
        本次需要用到的jar包主要有如下几个
        jar
  • 修改shiro.ini配置文件和web.xml配置文件

    配置文件中指定了寻找DatabaseRealm的方法、指定了每个页面需要什么角色和权限、指定了如果没有权限将会跳转到哪个页面。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    [main] 
    #使用数据库进行验证和授权
    databaseRealm=com.shiro.DatabaseRealm
    securityManager.realms=$databaseRealm

    #当访问需要验证的页面,但是又没有验证的情况下,跳转到login.jsp
    authc.loginUrl=/login.jsp
    #当访问需要角色的页面,但是又不拥有这个角色的情况下,跳转到noroles.jsp
    roles.unauthorizedUrl=/noRoles.jsp
    #当访问需要权限的页面,但是又不拥有这个权限的情况下,跳转到noperms.jsp
    perms.unauthorizedUrl=/noPerms.jsp

    #users,roles和perms都通过前面知识点的数据库配置了
    [users]

    #urls用来指定哪些资源需要什么对应的授权才能使用
    [urls]
    #doLogout地址就会进行退出行为
    /doLogout=logout
    #login.jsp,noroles.jsp,noperms.jsp 可以匿名访问
    /login.jsp=anon
    /noroles.jsp=anon
    /noperms.jsp=anon

    #阅读博客,需要登录后才可以查看
    /readBlog.jsp=authc
    #新增博客不仅需要登录,而且要拥有 blogManager 角色才可以操作
    /addBlog.jsp=authc,roles[blogManager]
    #删除博客,不仅需要登录,而且要拥有 deleteBlog 权限才可以操作
    /deleteBlog.jsp=authc,perms["deleteBlog"]

    web.xml配置了加载shiro.ini的配置
    <web-app>中配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    <listener>
    <listener-class>org.apache.shiro.web.env.EnvironmentLoaderListener</listener-class>
    </listener>
    <context-param>
    <param-name>shiroEnvironmentClass</param-name>
    <param-value>org.apache.shiro.web.env.IniWebEnvironment</param-value>
    <!-- 默认先从/WEB-INF/shiro.ini,如果没有找classpath:shiro.ini -->
    </context-param>
    <context-param>
    <param-name>shiroConfigLocations</param-name>
    <param-value>classpath:shiro.ini</param-value>
    </context-param>
    <filter>
    <filter-name>shiroFilter</filter-name>
    <filter-class>org.apache.shiro.web.servlet.ShiroFilter</filter-class>
    </filter>
    <filter-mapping>
    <filter-name>shiroFilter</filter-name>
    <url-pattern>/*</url-pattern>
    </filter-mapping>
  • Servlet

    新建一个LoginServlet,负责控制登录验证。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    @WebServlet(name = "loginServlet", urlPatterns = "/login") 
    public class LoginServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    String name = request.getParameter("name");
    String password = request.getParameter("password");
    Subject subject = SecurityUtils.getSubject();
    UsernamePasswordToken token = new UsernamePasswordToken(name, password);
    try {
    subject.login(token);
    //通过subject获取session
    Session session=subject.getSession();
    session.setAttribute("subject", subject);
    response.sendRedirect("");
    }catch (AuthenticationException e) {
    request.setAttribute("error", "验证失败");
    request.getRequestDispatcher("login.jsp").forward(request, response);
    }
    }
    }
  • Jsp前台页

    • index.jsp

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
      <html>
      <head>
      <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

      <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>

      <link rel="stylesheet" type="text/css" href="static/css/style.css" />

      </head>
      <body>

      <div class="workingroom">
      <div class="loginDiv">
      <c:if test="${empty subject.principal}">
      <a href="login.jsp">登录</a><br>
      </c:if>
      <c:if test="${!empty subject.principal}">
      <span class="desc">你好,${subject.principal},</span>
      <a href="doLogout">退出</a><br>
      </c:if>
      <a href="readBlog.jsp">查看博客</a><span class="desc">(登录后才可以查看) </span><br>
      <a href="addBlog.jsp">新增博客</a><span class="desc">(要有博客管理员角色, Reader是读者,Object是博客管理员) </span><br>
      <a href="deleteBlog.jsp">删除博客</a><span class="desc">(要有删除订单权限, Object有该权限) </span><br>
      </div>

      </body>
      </html>
    • login.jsp

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      <%@ page language="java" contentType="text/html; charset=UTF-8"
      pageEncoding="UTF-8" import="java.util.*"%>

      <!DOCTYPE html>

      <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
      <link rel="stylesheet" type="text/css" href="static/css/style.css" />

      <div class="workingroom">

      <div class="errorInfo">${error}</div>
      <form action="login" method="post">
      账号: <input type="text" name="name"> <br>
      密码: <input type="password" name="password"> <br>
      <br>
      <input type="submit" value="登录">
      <br>
      <br>
      <div>
      <span class="desc">账号:Object 密码:123456 角色:blogManager</span><br>
      <span class="desc">账号:Reader 密码:654321 角色:reader</span><br>
      </div>
      </form>
      </div>
    • readBlog.jsp

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      <%@ page language="java" contentType="text/html; charset=UTF-8"
      pageEncoding="UTF-8" import="java.util.*"%>

      <!DOCTYPE html>

      <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
      <link rel="stylesheet" type="text/css" href="static/css/style.css" />
      <div class="workingroom">
      readBlog.jsp ,能进来,就表示已经登录成功了
      <br>
      <a href="#" onClick="javascript:history.back()">返回</a>
      </div>
    • addBlog.jsp

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      <%@ page language="java" contentType="text/html; charset=UTF-8"
      pageEncoding="UTF-8" import="java.util.*"%>

      <!DOCTYPE html>

      <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
      <link rel="stylesheet" type="text/css" href="static/css/style.css" />

      <div class="workingroom">
      addBlog.jsp,能进来<br>就表示拥有 blogManager 角色
      <br>
      <a href="#" onClick="javascript:history.back()">返回</a>
      </div>
    • deleteBlog.jsp

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      <%@ page language="java" contentType="text/html; charset=UTF-8"
      pageEncoding="UTF-8" import="java.util.*"%>
      <!DOCTYPE html>
      <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
      <link rel="stylesheet" type="text/css" href="static/css/style.css" />

      <div class="workingroom">
      deleteBlog.jsp ,能进来,就表示有deleteBlog权限
      <br>
      <a href="#" onClick="javascript:history.back()">返回</a>
      </div>
    • noRoles.jsp

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      <%@ page language="java" contentType="text/html; charset=UTF-8"
      pageEncoding="UTF-8" import="java.util.*"%>
      <!DOCTYPE html>
      <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
      <link rel="stylesheet" type="text/css" href="static/css/style.css" />
      <div class="workingroom">
      角色不匹配
      <br>
      <a href="#" onClick="javascript:history.back()">返回</a>
      </div>
    • noPerms.jsp

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      <%@ page language="java" contentType="text/html; charset=UTF-8"
      pageEncoding="UTF-8" import="java.util.*"%>
      <!DOCTYPE html>
      <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
      <link rel="stylesheet" type="text/css" href="static/css/style.css" />
      <div class="workingroom">
      权限不足
      <br>
      <a href="#" onClick="javascript:history.back()">返回</a>
      </div>
    • style.css(页面样式)

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      span.desc{
      margin-left:20px;
      color:gray;
      }
      div.workingroom{
      margin:200px auto;
      width:400px;
      }
      div.workingroom a{
      display:inline-block;
      margin-top:20px;
      }
      div.loginDiv{
      text-align: left;
      }
      div.errorInfo{
      color:red;
      font-size:0.65em;
      }
  • 测试

    打开tomcat服务器,在浏览器url输入:localhost:8080/ShiroWeb
    登录界面
    登录Object
    登录成功
    点击各功能:
    登录访问成功
    访问成功
    权限访问成功
    登录Reader后点击各功能:
    除了ReadBlog可以进以外,其余都失败
    登录访问成功
    权限访问失败
    角色访问失败

关于整合SSM

  • 使用@RequireRoles注解

    核心:
    SSM注解
    这种方式只需要在Controller映射页面的方法上加上@RequireRoles(“需要的权限”)就可以轻松控制页面所需的权限了,但是在真实开发中,如果权限改变,那么你就需要一直去修改源代码,这样显然不合适,所以这种方式一带而过,可以在这里了解:Shiro系列教材

  • 使用基于URL配置权限

    这种方式主要是不用在每个页面的映射上都加所需的权限,而是动态地将角色信息和权限信息写入数据库,再读取数据库,看页面是否需要拦截,访问页面需要什么权限等。

    • 从零搭建
      首先先修改表结构

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      DROP DATABASE IF EXISTS shiro;
      CREATE DATABASE shiro DEFAULT CHARACTER SET utf8;
      USE shiro;

      drop table if exists user;
      drop table if exists role;
      drop table if exists permission;
      drop table if exists user_role;
      drop table if exists role_permission;

      create table user (
      id bigint auto_increment,
      name varchar(100),
      password varchar(100),
      salt varchar(100),
      constraint pk_users primary key(id)
      ) charset=utf8 ENGINE=InnoDB;

      create table role (
      id bigint auto_increment,
      name varchar(100),
      desc_ varchar(100),
      constraint pk_roles primary key(id)
      ) charset=utf8 ENGINE=InnoDB;

      create table permission (
      id bigint auto_increment,
      name varchar(100),
      desc_ varchar(100),
      url varchar(100),
      constraint pk_permissions primary key(id)
      ) charset=utf8 ENGINE=InnoDB;

      create table user_role (
      id bigint auto_increment,
      uid bigint,
      rid bigint,
      constraint pk_users_roles primary key(id)
      ) charset=utf8 ENGINE=InnoDB;

      create table role_permission (
      id bigint auto_increment,
      rid bigint,
      pid bigint,
      constraint pk_roles_permissions primary key(id)
      ) charset=utf8 ENGINE=InnoDB;

      实际上上面这段代码就是对原来的表新增了几个字段,新增字段如下:
      permission:desc_url
      role:desc_

      插入表数据:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      INSERT INTO `permission` VALUES (1,'addblog','新增博客','/addBlog');
      INSERT INTO `permission` VALUES (2,'readerBlog','阅读博客','/readBlog');
      INSERT INTO `role` VALUES (1,'blogManager','博客管理员');
      INSERT INTO `role` VALUES (2,'reader','读者');
      INSERT INTO `role_permission` VALUES (1,1,1);
      INSERT INTO `role_permission` VALUES (2,2,2);
      INSERT INTO `user` VALUES (1,'Object','a7d59dfc5332749cb801f86a24f5f590','e5ykFiNwShfCXvBRPr3wXg==');
      INSERT INTO `user` VALUES (2,'Reader','43e28304197b9216e45ab1ce8dac831b','jPz19y7arvYIGhuUjsb6sQ==');
      INSERT INTO `user_role` VALUES (1,2,2);
      INSERT INTO `user_role` VALUES (2,1,1);
    • 配置文件web.xml
      在web.xml文件中要配置四块内容,分别是,中文过滤器Spring相关MVC相关Shiro过滤器相关
      先说说shiro过滤器

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      <!-- Shiro-->
      <filter>
      <filter-name>shiroFilter</filter-name>
      <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
      <init-param>
      <param-name>targetFilterLifecycle</param-name>
      <param-value>true</param-value>
      </init-param>
      </filter>
      <filter-mapping>
      <filter-name>shiroFilter</filter-name>
      <url-pattern>/*</url-pattern>
      </filter-mapping>

      shiro过滤器默认拦截所有请求

      Spring相关:主要有两个,一个是Spring整合Mybatis,一个是Spring整合Shiro

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
       <!-- spring -->
      <context-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>
      classpath:applicationContext.xml,
      classpath:applicationContext-shiro.xml
      </param-value>
      </context-param>
      <listener>
      <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
      </listener>

      MVC相关:
      就是日常的 MVC配置

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      <!-- spring mvc -->
      <servlet>
      <servlet-name>mvc-dispatcher</servlet-name>
      <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
      <!-- spring mvc-->
      <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:springMVC.xml</param-value>
      </init-param>
      <load-on-startup>1</load-on-startup>
      </servlet>
      <servlet-mapping>
      <servlet-name>mvc-dispatcher</servlet-name>
      <url-pattern>/</url-pattern>
      </servlet-mapping>
    • Spring中关于Shiro的配置:
      1.SecurityManager:Shiro的核心安全管理类。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      <!-- 安全管理器 -->
      <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
      <property name="realm" ref="databaseRealm" />
      <property name="sessionManager" ref="sessionManager" />
      </bean>
      <!-- 相当于调用SecurityUtils.setSecurityManager(securityManager) -->
      <bean
      class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
      <property name="staticMethod"
      value="org.apache.shiro.SecurityUtils.setSecurityManager" />
      <property name="arguments" ref="securityManager" />
      </bean>

      2.HashedCredentialsMatcher:密码匹配器,可散列

      1
      2
      3
      4
      5
      6
       <!-- 密码匹配器 -->
      <bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
      <property name="hashAlgorithmName" value="md5"/>
      <property name="hashIterations" value="2"/>
      <property name="storedCredentialsHexEncoded" value="true"/>
      </bean>

      3.LifecycleBeanPostProcessor:保证了Shiro内部lifecycle函数的执行

      1
      2
      <!-- 保证实现了Shiro内部lifecycle函数的bean执行 -->
      <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />

      4.ShiroFilterFactoryBean:shiro的过滤器工厂类

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
       <!-- url过滤器 -->          
      <bean id="urlPathMatchingFilter" class="com.shiro.filter.URLPathMatchingFilter"/>

      <!-- 配置shiro的过滤器工厂类,id- shiroFilter要和我们在web.xml中配置的过滤器一致 -->
      <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
      <!-- 调用我们配置的权限管理器 -->
      <property name="securityManager" ref="securityManager" />
      <!-- 配置我们的登录请求地址 -->
      <property name="loginUrl" value="/login" />
      <!-- 如果您请求的资源不再您的权限范围,则跳转到/403请求地址 -->
      <property name="unauthorizedUrl" value="/unauthorized" />
      <!-- 退出 -->
      <property name="filters">
      <util:map>
      <entry key="logout" value-ref="logoutFilter" />
      <entry key="url" value-ref="urlPathMatchingFilter" />
      </util:map>
      </property>
      <!-- 权限配置 -->
      <property name="filterChainDefinitions" ref="filterChainDefinitions"/>
      </bean>
      <!-- 退出过滤器 -->
      <bean id="logoutFilter" class="org.apache.shiro.web.filter.authc.LogoutFilter">
      <property name="redirectUrl" value="/index" />
      </bean>

      5.FilterChainDefinitions:配置可从数据库中读取页面权限的LinkedHashMap

      1
      2
      3
      <bean id="filterChainDefinitions" factory-bean="filterChainDefinitionsFactory" factory-method="buildFilterChainDefinitionMap"></bean>
      <bean id="filterChainDefinitionsFactory" class="com.shiro.entity.FilterChainDefinitions">
      </bean>

      6.会话相关

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      <!-- 会话ID生成器 -->
      <bean id="sessionIdGenerator"
      class="org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator" />
      <!-- 会话Cookie模板 关闭浏览器立即失效 -->
      <bean id="sessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
      <constructor-arg value="sid" />
      <property name="httpOnly" value="true" />
      <property name="maxAge" value="-1" />
      </bean>
      <!-- 会话DAO -->
      <bean id="sessionDAO"
      class="org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO">
      <property name="sessionIdGenerator" ref="sessionIdGenerator" />
      </bean>
      <!-- 会话验证调度器,每30分钟执行一次验证 ,设定会话超时及保存 -->
      <bean name="sessionValidationScheduler"
      class="org.apache.shiro.session.mgt.ExecutorServiceSessionValidationScheduler">
      <property name="interval" value="1800000" />
      <property name="sessionManager" ref="sessionManager" />
      </bean>
      <!-- 会话管理器 -->
      <bean id="sessionManager"
      class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
      <!-- 全局会话超时时间(单位毫秒),默认30分钟 -->
      <property name="globalSessionTimeout" value="1800000" />
      <property name="deleteInvalidSessions" value="true" />
      <property name="sessionValidationSchedulerEnabled" value="true" />
      <property name="sessionValidationScheduler" ref="sessionValidationScheduler" />
      <property name="sessionDAO" ref="sessionDAO" />
      <property name="sessionIdCookieEnabled" value="true" />
      <property name="sessionIdCookie" ref="sessionIdCookie" />
      </bean>

      7.自定义Realm

      1
      2
      3
      <bean id="databaseRealm" class="com.shiro.service.impl.DatabaseRealm">
      <property name="credentialsMatcher" ref="credentialsMatcher"/>
      </bean>
    • 核心代码:
      因为这是基于url的权限管理,所以,就不会再有ini配置文件了(实际上配置到Spring中),Realm直接从数据库中获取用户的信息,给用户做验证和授权操作。
      最核心的代码还是自定义Realm:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      public class DatabaseRealm extends AuthorizingRealm {

      @Autowired
      private UserService userService;
      @Autowired
      private RoleService roleService;
      @Autowired
      private PermissionService permissionService;

      @Override
      protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
      //获取用户名
      String userName = (String) principalCollection.getPrimaryPrincipal();
      //从数据库中获取角色和权限
      Set<String> permissions = permissionService.getStringPermissionByName(userName);
      Set<String> roles = roleService.listPermissionURLs(userName);

      //建立简单授权对象
      SimpleAuthorizationInfo s = new SimpleAuthorizationInfo();
      //设置权限和角色
      s.setStringPermissions(permissions);
      s.setRoles(roles);
      //授权
      return s;
      }

      @Override
      protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
      UsernamePasswordToken t = (UsernamePasswordToken) token;
      String userName = token.getPrincipal().toString();
      User user = userService.getUser(userName);
      String passwordInDB = user.getPassword();
      System.out.println(passwordInDB);
      System.out.println(t.getPassword());
      String salt = user.getSalt();
      //做用户验证
      SimpleAuthenticationInfo a = new SimpleAuthenticationInfo(userName, passwordInDB, ByteSource.Util.bytes(salt),
      getName());
      return a;
      }
      }

      Subject会将用户的信息交给上述Realm做验证和授权,做验证很好理解,登录即验证,但是要怎么判断一个url是否要做权限验证呢?
      在PermissionService中有两个方法,分别是needIntercepterlistPermissionURLs,第一个方法的作用是判断一个url是否要验证,如果权限表中有这个url,则需要进行授权,如果没有则直接放行,listPermissionURLs是判断一个用户有权访问的所有url

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
       @Override
      public boolean needInterceptor(String requestURI) {
      // TODO Auto-generated method stub
      List<Permission> permissionList = permissionDao.listPermission();
      for(Permission p : permissionList) {
      if(p.getUrl().equals(requestURI)) {
      return true;
      }
      }
      return false;
      }

      @Override
      public Set<String> listPermissionURLs(String userName) {
      // TODO Auto-generated method stub
      Set<String> result = new HashSet<>();
      List<Permission> permissions = permissionDao.queryPermissionByUsername(userName);
      for(Permission p : permissions) {
      result.add(p.getUrl());
      }
      return result;
      }

      于是,在过滤器和Realm中就可以调用这两个方法判断是否需要授权和进行授权了。
      过滤器类:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
       public class URLPathMatchingFilter extends PathMatchingFilter {
      @Autowired
      PermissionService permissionService;

      @Override
      protected boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue)
      throws Exception {
      String requestURI = getPathWithinApplication(request);

      System.out.println("requestURI:" + requestURI);

      Subject subject = SecurityUtils.getSubject();
      // 如果没有登录,就跳转到登录页面
      if (!subject.isAuthenticated()) {
      WebUtils.issueRedirect(request, response, "/login");
      return false;
      }

      // 看看这个路径权限里有没有维护,如果没有维护,一律放行(也可以改为一律不放行)
      boolean needInterceptor = permissionService.needInterceptor(requestURI);
      if (!needInterceptor) {
      return true;
      } else {
      //如果有维护判断是否有权限访问(进入Realm进行授权)
      if (subject.isPermitted(requestURI))
      return true;
      else {
      UnauthorizedException ex = new UnauthorizedException("当前用户没有访问路径 " + requestURI + " 的权限");

      subject.getSession().setAttribute("ex", ex);

      WebUtils.issueRedirect(request, response, "/unauthorized");
      return false;
      }
      }
      }
      }
  • 最后运行结果

    在运行最后结果之前,先明确一下数据表中用户的角色与权限。
    权限关系
    所以运行结果如下:

    • 登录Object
      登录Object
    • Object登录成功
      Object登录成功
    • Object访问addBlog
      Object访问增加博客
    • Object访问readBlog
      Object访问阅读博客
    • 登录Reader
      登录Reader
    • Reader访问addBlog
      Reader访问增加博客
    • Reader访问readBlog
      Reader访问阅读博客

结语

两天时间学完Shiro并写完了自己的学习笔记,从网上找资料学习到遇到各种坑,到差点偏离Shiro的方向,好在还是完成了Shiro的修炼,总算理解了这一系列验证及授权的流程,之后应该将Shiro结合项目使用,并去理解其原理和工作流程。

参考资料:Shiro系列教材

欢迎大家访问我的个人博客:Object’s Blog

原文作者:Object

原文链接:http://blog.objectspace.cn/2019/08/21/关于Shiro框架的学习(二)/

发表日期:2019 August 21st, 11:49:37 pm

更新日期:2019 September 27th, 1:50:28 pm

版权声明:未经作者授权请勿转载

目录
  1. 1. 前言
  2. 2. 关于整合Web
    1. 2.1. 环境
    2. 2.2. 准备工作
    3. 2.3. 修改shiro.ini配置文件和web.xml配置文件
    4. 2.4. Servlet
    5. 2.5. Jsp前台页
    6. 2.6. 测试
  3. 3. 关于整合SSM
    1. 3.1. 使用@RequireRoles注解
    2. 3.2. 使用基于URL配置权限
    3. 3.3. 最后运行结果
  4. 4. 结语