springboot与shiro实现权限管理系统(三)——引入Shiro并进行配置

  • 本篇博客知识点:
    1. 引入 Shiro 的 maven 依赖
    2. 实现自己的 Realm
    3. 配置 Shiro 过滤器

准备工作

1. 配置

  1. 引入 Shiro 的 maven 依赖
    在最初创建项目时我们在 pom.xml 中引入了下面的 Shiro 依赖:

    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-spring</artifactId>
        <version>1.5.3</version>
    </dependency>
    

    这是使用原生 Shiro 进行集成的方式,针对 Spring Boot 应用,Shiro 官方提供了专门的 Starter:shiro-spring-boot-web-starter来帮助开发者简化在 Spring Boot 应用中的集成过程。

    这里我们将采用 Starter 的方式,将上面的原生依赖替换为下面的 Starter 依赖:

    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-spring-boot-web-starter</artifactId>
        <version>1.4.0</version>
    </dependency>
    
  2. .../main/resources/application.yml 配置文件中对 Shiro 进行配置。

    shiro:
      web:
        enabled: true
      sessionManager:
        sessionIdCookieEnabled: true
        sessionIdUrlRewritingEnabled: true
      loginUrl: /login.html
    
  3. 实现 ACSRealm

    在 …/com/code3/admin/config 目录下定义 ACSRealm 类,用来实现我们自己的 Realm,ACSRealm 将继承 AuthorizingRealm 。AuthorizingRealm 定义了两个抽象方法:doGetAuthorizationInfo 和 doGetAuthenticationInfo ,这两个方法是 Shiro 进行身份认证和授权的关键。

    // ...
    @Autowired
    private SysUserDao sysUserDao;
    
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String username = (String) token.getPrincipal();
        SysUser user = sysUserDao.findByUsername(username);
        if (user == null) {
            throw new UnknownAccountException();
        }
    
        String pwd = user.getPassword();
        String password = new String(((char[]) token.getCredentials()));
        if (!pwd.equals(password)) {
            throw new IncorrectCredentialsException();
        }
        return new SimpleAuthenticationInfo(username, pwd, getName());
    }
    // ...
    
    // ...
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        Collection<Permission> permissions = loadPermissions(principals);
        authorizationInfo.addObjectPermissions(permissions);
        return authorizationInfo;
    }
    
    private Collection<Permission> loadPermissions(PrincipalCollection principals) {
        String username = (String) principals.getPrimaryPrincipal();
        List<String> ps = sysUserDao.findAllPermissions(username);
        if (CollectionUtils.isEmpty(ps)) {
            return null;
        }
        return ps.stream().map(WildcardPermission::new).collect(Collectors.toList());
    }
    // ...
    

    对上述doGetAuthenticationInfo代码进行解析:

    1. doGetAuthenticationInfo 是 Shiro 的一个回调,用于获取用户的身份信息,期间需要先将用户输入的凭证数据和用户预先保存数据库中的凭证数据进行比较,以确定是用户“本人”,该步骤决定了用户登录(SecurityUtils.getSubject().login()方法调用)是否能够成功。

    2. 参数 AuthenticationToken 中封装了用户输入的凭证数据,在 doGetAuthenticationInfo 方法中,首先要做的是找到用户预先保存在数据库中的凭证数据,这里借助用户输入的 username 进行查找(sysUserDao.findByUsername(username)),实际开发中也可能是其它信息,如手机号,邮箱等,而无论实际载体是什么,只要能通过它唯一找到保存在数据库中的用户身份凭证数据即可。如果没找到,说明用户不存在,此时需要抛出 Shiro 为我们定义好的 UnknownAccountException 异常,通过在 SecurityUtils.getSubject().login() 的调用方法中捕获这些异常,我们就能知道登录失败的原因。如果在数据库中成功找到凭证数据,紧接着就要对用户输入的凭证数据和数据库中查询到的凭证数据进行比较,以得出用户是否输入正确 password 的结论,用户输入错误的密码时抛出 IncorrectCredentialsException 异常。最后,如果登录成功,将用户身份信息封装成一个 AuthenticationInfo 返回,同时将控制权交回给 Shiro,让 Shiro 处理之后的事情。

    3. 在此系统中,我们将会把密码以明文的方式保存到数据库中,以节省开发时间,把更多时间放在 Shiro 的使用上,实际开发中应该对密码进行加密,避免明文存储,此外 Shiro 提供了 CredentialsMatcher 接口类规范了密码校验过程,有 PasswordMatcherHashedCredentialsMatcher 等实现可供使用。

    对上述doGetAuthorizationInfo代码进行解析:

    1. doGetAuthorizationInfo 方法用于获取用户的权限信息;
    2. 方法findAllPermissions 的位置: resources/mapper/SysUserMapper.xml
      <select id="findAllPermissions" resultType="java.lang.String">
          SELECT sp.permission_code
          FROM sys_user su
                   LEFT JOIN sys_user_role sur ON su.id = sur.user_id
                   LEFT JOIN sys_role_permission srp ON sur.role_id = srp.role_id
                   LEFT JOIN sys_permission sp ON srp.permission_id = sp.id
          WHERE username = #{username}
      </select>
      
  4. 配置 SecurityManager : .../com/code3/admin/config/ShiroConfig.java

    //将自己的ACSRealm配置给 SecurityManager
    
    @Bean
    DefaultWebSecurityManager securityManager(ACSRealm realm) {
        DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
        manager.setRealm(realm);
        return manager;
    }
    
  5. 配置shiro过滤器

    // Shiro 过滤器可以指定对 URL 路径应用什么样的访问控制策略,如控制某一 URL 允许匿名访问,或者需要完成身份认证(登录)后才能访问等.
    
    @Bean
    ShiroFilterChainDefinition shiroFilterChainDefinition() {
        DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition();
        // swagger 文档相关的接口允许匿名访问
        chainDefinition.addPathDefinition("/swagger**", "anon");
        chainDefinition.addPathDefinition("/webjars**", "anon");
    
        // 登录接口允许匿名访问
        chainDefinition.addPathDefinition("/api/login", "anon");
    
        // 所有 api 开头的接口都需要登录
        chainDefinition.addPathDefinition("/api/**", "authc");
        return chainDefinition;
    }
    

    我们使用 Swagger 作为文档和接口测试工具,所以将 Swagger-UI 需要访问的两类接口(/swagger** 和 /webjars**)设置为允许匿名访问;登录接口 URL 为 /api/login,允许匿名访问(下一节实验中我们会进行实现),剩余的所有以/api/ 开头的接口需要登录后才能访问。

    接下来开发接口时我们将为所有接口的 URL 添加一个统一的 /api 前缀。

2. 本篇知识点补充

01 | Shiro的配置
  1. web.enabled 控制是否启用 Shiro 框架,true 表示启用,在项目开发阶段为了跳过安全检查可以设置为 false。
  2. sessionManager.sessionIdCookieEnabled:启用或停用通过 cookie 的会话状态保持,false 表示不允许将 sessionID(实际上是 JSESSIONID)放到 cookie 中。
  3. sessionManager.sessionIdUrlRewritingEnabled:是否允许将 sessionID 放到 URL 中,以 URL 参数的方式进行传递,当sessionManager.sessionIdCookieEnabled 设置为 true 时,该项可以设为 false。
  4. loginUrl 参数用来指定登录页面,当用户访问没有权限的 URL 时将自动跳转到该页面,login.html 的内容我们将在下一节实验中实现。
02 | 关于Realm
  • Realm 是 Shiro 的核心组件,是用户身份信息,权限以及角色信息的提供者,从数据库中获取数据,然后转交给 Shiro 进行身份认证和权限管理,开发中一般基于其抽象子类 AuthorizingRealm 进行扩展,以减少工作量。
03 | 关于过滤器
  • 常见的过滤器有下面几种:
    1. anon:允许匿名访问。
    2. authc:需要完成身份认证(登录)后才能访问。
    3. perms:可指定访问者必须具备的权限,如:“perms[role:add, user:list]”,表示访问者需要同时具备 “role:add” 和 “user:list” 权限才能访问当前路径。
    4. user:必须存在用户,身份认证通过或通过“记住我”认证通过的都可以访问。

完成。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值