Shiro权限控制

认识Shiro的整体架构,各组件的概念

简介

  • Apache 的强大灵活的开源安全框架;
  • 认证、授权、企业会话管理、安全加密;

Shiro与Spring Security比较

Apache ShiroSpring Security
简单灵活复杂笨重
可脱离Spring不可脱离Spring
粒度较粗粒度更细

Shiro整体架构

Shiro通过Security Manager提供安全服务
Security Manager管理着其他组件的实例

  1. Authenticator(认证器):管理登录,登出
  2. Authorizer(授权器):赋予主体权限
  3. Session Manager(Session管理器):Shiro自己实现的管理机制,不借用任何容器使用Session
  4. Session Dao(提供Session的操作):主要有:增删改查。
  5. Cache Manager(缓存管理器):角色和权限数据缓存。
  6. Pauggable Realms(数据库与数据源的桥梁):shiro获取数据是通过realms来获取的。

流程:

  1. 主体提交请求到Security Manager.
  2. Security Manager调用Authenticator进行认证。(Authenticator认证获取数据是通过realms获取的,再从数据源中获取信息)数据源信息和主体提交的信息比对。
  3. (Authorizer授权获取数据是通过realms获取的,再从数据源中获取数据)数据源信息和主体提交的信息做比对。
  4. 数据加密
    在这里插入图片描述

Shiro认证,授权的过程

shiro认证

Shiro安全认证简单流程如图:

  1. 构建SecurityManager环境
  2. 主体提交认证请求
  3. SecurityManager认证
  4. Authenticator认证
  5. Realm验证(用户名,密码信息)

注:3,4,5步认证在主体提交认证里面

package org.example;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.realm.SimpleAccountRealm;
import org.apache.shiro.subject.Subject;
import org.junit.Before;
import org.junit.Test;

public class AuthenticationTest {

    public SimpleAccountRealm simpleAccountRealm = new SimpleAccountRealm();

    @Before
    public void addUser(){
        simpleAccountRealm.addAccount("mark", "123456");
    }


    @Test
    public void testAuthentication(){
        //1.构建SecurityManager环境
        DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
        defaultSecurityManager.setRealm(simpleAccountRealm);

        //2. 主体提交认证请求
        SecurityUtils.setSecurityManager(defaultSecurityManager);
        Subject subject = SecurityUtils.getSubject();

        UsernamePasswordToken token = new UsernamePasswordToken("mark", "123456");
        subject.login(token);

        System.out.println("isAuthenticated:" + subject.isAuthenticated());


    }
}

Shiro授权

Shiro授权
与之前Shiro认证的步骤一样。
只不过在Realm 的SimpleAccountRealm中可以添加addUser的时候,可以添加入多个角色(即可变数组的形式)
同样进行授权验证即检验该登录用户是否具备该角色的时候,使用:
subject.checkRoles(可变参数)的形式检验。
必须在登录的情况下,其他步骤与认证相同。

package org.example;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.realm.SimpleAccountRealm;
import org.apache.shiro.subject.Subject;
import org.junit.Before;
import org.junit.Test;

public class AuthenticationTest {

    public SimpleAccountRealm simpleAccountRealm = new SimpleAccountRealm();

    @Before
    public void addUser(){
        simpleAccountRealm.addAccount("mark", "123456", "admin", "user");
    }


    @Test
    public void testAuthentication(){
        //1.构建SecurityManager环境
        DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
        defaultSecurityManager.setRealm(simpleAccountRealm);

        //2. 主体提交认证请求
        SecurityUtils.setSecurityManager(defaultSecurityManager);
        Subject subject = SecurityUtils.getSubject();

        UsernamePasswordToken token = new UsernamePasswordToken("mark", "123456");
        subject.login(token);

        System.out.println("isAuthenticated:" + subject.isAuthenticated());

        subject.checkRoles("admin", "user");

    }
}

Shiro自定义的Realm,Filter

IniRealm配置文件

IniRealm就是把 realm 的信息以.ini的配置文件形式保存,
其他的和认证授权没啥区别

package org.example;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.realm.text.IniRealm;
import org.apache.shiro.subject.Subject;
import org.junit.Test;

public class IniRealmTest {



    @Test
    public void testAuthentication(){

        //获取realm配置文件
        IniRealm iniRealm = new IniRealm("classpath:user.ini");

        //1.构建SecurityManager环境
        DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
        defaultSecurityManager.setRealm(iniRealm);


        //2. 主体提交认证请求
        SecurityUtils.setSecurityManager(defaultSecurityManager);
        Subject subject = SecurityUtils.getSubject();

        UsernamePasswordToken token = new UsernamePasswordToken("mark", "123456");
        subject.login(token);

        System.out.println("isAuthenticated:" + subject.isAuthenticated());

        subject.checkRoles("admin", "user");

    }

}

ini配置文件

[users]
mark=123456,admin,user
[roles]
admin=user:delete,user:update

JdbcRealm

JdbcRealm就是把 realm 的信息从数据库里去查找,默认有一些JdbcRealm自带有一些默认的SQL。realm的信息主要有用户信息users角色信息user_roles角色的权限信息roles_permissions
验证信息的SQL也可以自定义编写
注意:权限校验要在jdbcRealm里开启权限

package org.example;

import com.alibaba.druid.pool.DruidDataSource;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.realm.jdbc.JdbcRealm;
import org.apache.shiro.realm.text.IniRealm;
import org.apache.shiro.subject.Subject;
import org.junit.Test;

public class JdbcRealmTest {

    DruidDataSource dataSource = new DruidDataSource();

    {
        dataSource.setUrl("jdbc:mysql://localhost:3306/shiro_demo?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8&autoReconnect=true");
        dataSource.setUsername("root");
        dataSource.setPassword("root");
    }

    @Test
    public void testAuthentication(){

        JdbcRealm jdbcRealm = new JdbcRealm();
        jdbcRealm.setDataSource(dataSource);
        jdbcRealm.setPermissionsLookupEnabled(true);

        //自定义用户信息验证SQL
        String userSql = "select password from users_test where username= ?";
        jdbcRealm.setAuthenticationQuery(userSql);

        //自定义角色验证SQL
        String roleSql = "select role_name from user_roles_test where username = ?";
        jdbcRealm.setUserRolesQuery(roleSql);

        //自定义角色权限验证SQL
        String permissionSql = "select permission from roles_permissions_test where role_name =?";
        jdbcRealm.setPermissionsQuery(permissionSql);

        //1.构建SecurityManager环境
        DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
        defaultSecurityManager.setRealm(jdbcRealm);


        //2. 主体提交认证请求
        SecurityUtils.setSecurityManager(defaultSecurityManager);
        Subject subject = SecurityUtils.getSubject();

//        UsernamePasswordToken token = new UsernamePasswordToken("mark", "123456");
        UsernamePasswordToken token = new UsernamePasswordToken("rose", "111222");
        subject.login(token);

        System.out.println("isAuthenticated:" + subject.isAuthenticated());

//        subject.checkRoles("admin");
        subject.checkRoles("user");

//        subject.checkPermission("user:select");
        subject.checkPermission("user:update");

    }

}

自定义Realm

自定义的realm,要注意以下几个步骤:

  1. 首先继承并实现类AuthorizingRealm的方法。其中方法doGetAuthenticationInfo主要做认证操作,即可以通过用户名,查询出相应的密码,然后将用户名与密码一同返回,shiro会自动根据传入的用户名和密码与此realm返回的用户名和密码做比对,返回你想要的结果。
    同理,doGetAuthorizationInfo主要是用来做角色和权限的验证,也是通过用户名,从数据库中查找到相应的角色或者权限的数据并返回一个simple的授权类,授权系统会根据传入的参数与返回的结果集对比,存在返回TRUE,不存在则抛出异常。
  2. 两者方法只能获取用户名称,通过用户名称连接数据库获取其他信息。这里只是做数据准备操作,并不做判断是否传入的值与其相符的操作,此操作在此之后进行
  3. 根据两者的返回对象分别返回simple类型的对象。认证的对象需要将你获取的用户名和密码放到改造方法中。授权对象需要set到相应的方法中。
package org.example.config;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthenticatingRealm;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

public class CustomRealm extends AuthorizingRealm {

    Map<String, String> usermap = new HashMap<String, String>();

    {
        usermap.put("mark", "123456");

        super.setName("customRealm");
    }


    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        String username = (String) principalCollection.getPrimaryPrincipal();
        //从数据库缓存中获取角色数据
        Set<String> roles = getRolesByUserName(username);
        
        //获取用户权限
        Set<String> permissions = getPermissionsByUserName(username);

        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        simpleAuthorizationInfo.setRoles(roles);
        simpleAuthorizationInfo.setStringPermissions(permissions);

        return simpleAuthorizationInfo;
    }
    
    private Set<String> getPermissionsByUserName(String userName) {
        Set<String> sets = new HashSet<String>();
        sets.add("user:delete");
        sets.add("user:add");
        return sets;
    }

    private Set<String> getRolesByUserName(String username) {
        Set<String> sets = new HashSet<String>();
        sets.add("admin");
        sets.add("user");
        return sets;
    }

    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        //1.从主体传过来的认证信息中,获得用户名
        String userName = (String) authenticationToken.getPrincipal();

        //2.通过用户名从数据库中获取凭证
        String password = getPasswordUsername(userName);
        if (password == null) {
            return null;
        }
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo("Mark", password, "customRealm");

        return authenticationInfo;
    }

    //模拟数据库查询凭证
    public String getPasswordUsername(String username){
        return usermap.get(username);
    }


}

shiro加密

Shiro散列配置

  • HashedCredentialsMatcher
    通过HashedCredentialsMaster的方式加密,创建对象,并设置加密次数以及加密名称,将此加密对象设置到Realm的setCredentialsmaster的方法中,即该Realm采用此密码加密的方式。
    后台的密码采用MD5的hash码值来存储。
    在这里插入图片描述

  • 盐的使用
    加盐的意思就是在密码中拼接其他字符串,组成一个新的密码,然后对这个密码进行md5加密。同时,可以在自定义realm里面的doGetAuthenticationInfo方法进行设置盐进行解密
    在这里插入图片描述

在这里插入图片描述

Shiro Session管理

Shiro 缓存管理

Shiro集成Spring

shiro集成spring

1,引入jar包

<dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.8</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.3.8</version>
        </dependency>

        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <version>1.8.0</version>
        </dependency>

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

2,新建一个xml文件配置shiro主体

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager" />
        <property name="loginUrl" value="login.html"/>
        <property name="unauthorizedUrl" value="403.html" />
        <property name="filterChainDefinitions" >
            <value>
                /login.html = anon
                /subLogin = anon
                /* = authc
            </value>
        </property>

    </bean>

<!--    创建SecurityManager对象-->
    <bean class="org.apache.shiro.web.mgt.DefaultWebSecurityManager" id="securityManager">
        <property name="realm" ref="realm" />
    </bean>

    <bean id="realm" class="org.example.shiro.realm.CustomRealm" >
        <property name="credentialsMatcher" ref="credentialsMatcher"/>
    </bean>

    <!--设置加密的算法-->
    <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher"
          id="credentialsMatcher">
        <property name="hashAlgorithmName" value="md5"/>
        <property name="hashIterations" value="1"/>
    </bean>

</beans>

通过注解的方式配置角色权限

  1. 开启aop设置TRUE;
  2. 添加LifecycleBeanPostProcessor;
  3. 添加AuthorizationAttributeSourceAdvisor
<aop:config proxy-target-class="true" />
    <bean class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />
    <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
        <property name="securityManager" ref="securityManager" />
    </bean>
  1. 接口上面配置注解
    @RequestMapping(value = "/test", method = RequestMethod.GET)
    @ResponseBody
    @RequiresRoles("admin")
    public String testRoles() {
        return "access";
    }

    @RequestMapping(value = "/test", method = RequestMethod.GET)
    @ResponseBody
    @RequiresPermissions("add")
    public String testPermissions() {
        return "access";
    }

shiro过滤器

shiro内置过滤器
角色相关:

  • anon 代表无需权限
  • authBasic
  • authc 代表需要认证才能访问
  • user 代表需要存在用户对象才能访问
  • logout 登录退出才能访问

权限相关:

  • perms 拥有权限才能被访问
  • roles 拥有角色才能被访问
  • ssl
  • port 相应端口号才能访问
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager" />
        <property name="loginUrl" value="login.html"/>
        <property name="unauthorizedUrl" value="403.html" />
        <property name="filterChainDefinitions" >
            <value>
                /login.html = anon
                /subLogin = anon
                /testRole = roles["admin"]
                /testRole1 = roles["admin","admin1"]
                /testPerms = perms["user:delete"]
                /testPerms1 = perms["user:delete","user:update"]
                /* = authc
            </value>
        </property>
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值