Базовый учебник Сиро

задняя часть Shiro

image

Введение

Apache Shiro — это фреймворк безопасности для Java. Мощная и простая в использовании платформа безопасности Java предоставляет разработчикам интуитивно понятное комплексное решение для аутентификации, авторизации, шифрования и управления сеансами.

2. Введение

2.1 Особенности

Широ содержит 10 элементов, как показано ниже:

image

1) Аутентификация: Аутентификация/вход в систему, проверка наличия у пользователя соответствующего идентификатора.

2) Авторизация: авторизация, то есть проверка авторитета, чтобы проверить, есть ли аутентифицированный пользователь определенный авторитет; то есть определить, может ли пользователь делать вещи, такие как: проверить, имеет ли определенный пользователь определенную роль. Или мелкозернистая проверка того, имеет ли пользователь определенное разрешение на определенный ресурс.

3) Диспетчер сессий: Управление сессиями, то есть после того, как пользователь входит в систему, это сессия, а перед выходом из системы вся его информация находится в сессии; сессия может быть в обычной среде JavaSE или в веб-среде.

4) Криптография: шифрование для защиты безопасности данных, таких как пароли, зашифрованы и хранятся в базе данных вместо открытого текста.

5) Веб-поддержка: веб-поддержка, вы можете очень легко интегрироваться в веб-среду.

6) Кэширование: Кэширование, например, после входа пользователя в систему, его пользовательскую информацию и роли/разрешения не нужно проверять каждый раз, что может повысить эффективность.

7) Параллелизм: shiro поддерживает параллельную проверку многопоточных приложений, то есть, если в одном потоке запускается другой поток, разрешения могут распространяться автоматически.

8) Тестирование: обеспечьте поддержку тестирования.

9) Запуск от имени: позволяет одному пользователю выдавать себя за другого пользователя (если он разрешает это) для доступа.

10) Запомнить меня: Запомнить меня, это очень распространенная функция, то есть авторизовавшись один раз, вам не нужно авторизоваться в следующий раз.

2.2 Принцип работы

Схема операции Широ 1 (с точки зрения применения) выглядит следующим образом:

image

1) Тема: Тема, представляющая текущего «пользователя». Этот пользователь не обязательно является конкретным человеком, все, что взаимодействует с текущим приложением, является субъектом, например, веб-краулер, робот и т. д. Все субъекты привязаны к SecurityManager, и все взаимодействия с субъектом делегируются SecurityManager. Мы можем думать о Subject как о фасаде, а SecurityManager — как о фактическом исполнителе.

2) SecurityManager: Менеджер безопасности. То есть обо всех операциях, связанных с безопасностью, будет сообщаться SecurityManager, и он управляет всеми субъектами. Видно, что это ядро ​​Широ, которое отвечает за взаимодействие с другими компонентами, представленными позже.Если вы изучили SpringMVC, мы можем рассматривать его как фронт-контроллер DispatcherServlet.

3) Царство: Царство. Shiro получает данные безопасности (такие как пользователи, роли, разрешения) из Realm, то есть, если SecurityManager хочет проверить личность пользователя, ему необходимо получить соответствующего пользователя из Realm для сравнения, чтобы определить, является ли личность пользователя законной, а также необходимо получить соответствующий идентификатор пользователя из Realm.Роли/разрешения используются для проверки того, может ли пользователь выполнять операции. Мы можем думать о Realm как об источнике данных, безопасном источнике данных.

Диаграмма принципа работы Shiro 2 (перспектива внутренней архитектуры Shiro) выглядит следующим образом:

image

1) Субъект: Субъект, вы видите, что субъектом может быть любой «пользователь», взаимодействующий с приложением.

2) SecurityManager: эквивалентно DispatcherServlet в SpringMVC или FilterDispatcher в StrUTS2. Это ядро ​​Широ, и все специфические взаимодействия контролируются через SecurityManager. Это управляет всеми предметами и отвечает за аутентификацию и авторизацию, а также управление сеансом и кешем.

3) Аутентификатор: Аутентификатор, отвечающий за аутентификацию субъекта, это точка расширения, если пользователи считают, что Широ по умолчанию не подходит, мы можем настроить реализацию. Для этого требуется стратегия аутентификации, то есть, при каких обстоятельствах проходит аутентификация пользователя.

4) Авторизатор: авторизатор или контроллер доступа. Он используется для определения того, имеет ли субъект разрешение на выполнение соответствующей операции, то есть контролирует, к каким функциям в приложении может получить доступ пользователь.

5) Область: может быть одна или несколько областей, которые можно рассматривать как источник данных объекта безопасности, то есть используемые для получения объекта безопасности. Это может быть реализация JDBC, реализация LDAP, реализация в памяти и т. д.

6) SessionManager: если вы написали сервлет, вы должны знать концепцию сеанса. Сеансу нужен кто-то, чтобы управлять его жизненным циклом. Этот компонент — SessionManager. И Широ можно использовать не только в веб-среде, но и в обычной среде JavaSE.

7) SessionDAO: DAO используется всеми, объекты доступа к данным, CRUD для сессий. Мы можем настроить реализацию SessionDAO, чтобы контролировать, где хранится сеанс. Например, запись в базу данных через JDBC или запись в Redis через jedis. Кроме того, Cache можно использовать в SessionDAO для кэширования с целью повышения производительности.

8) CacheManager: Менеджер кеша. Он управляет кэшами, такими как пользователи, роли, разрешения и т. д. Поскольку эти данные в основном редко изменяются, помещение их в кэш может повысить производительность доступа.

9) Криптография: Криптографический модуль Широ улучшает некоторые общие криптографические компоненты, такие как криптографическое шифрование/дешифрование.

2.3 Фильтры

Когда Широ применяется к веб-проекту, Широ автоматически создает некоторые фильтры по умолчанию для фильтрации клиентских запросов. Вот фильтры, предоставленные Широ:

аббревиатура фильтра Соответствующий класс Java
anon org.apache.shiro.web.filter.authc.AnonymousFilter
authc org.apache.shiro.web.filter.authc.FormAuthenticationFilter
authcBasic org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter
perms org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter
port org.apache.shiro.web.filter.authz.PortFilter
rest org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter
roles org.apache.shiro.web.filter.authz.RolesAuthorizationFilter
ssl org.apache.shiro.web.filter.authz.SslFilter
user org.apache.shiro.web.filter.authc.UserFilter
logout org.apache.shiro.web.filter.authc.LogoutFilter
noSessionCreation org.apache.shiro.web.filter.session.NoSessionCreationFilter

3. Основное введение

3.1 Добавить зависимости

<dependency>
    <groupId>commons-logging</groupId>
    <artifactId>commons-logging</artifactId>
    <version>1.1.3</version>
</dependency>
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-core</artifactId>
    <version>1.4.0</version>
</dependency>

3.2 Файл конфигурации

Создайте файл конфигурации с именем shiro.ini в каталоге src/main/resources со следующим содержимым:

[users]
# admin=admin 分别表示账号和密码,administrator 表示逗号前边的账号拥有 administrator 这个角色。
admin=admin,administrator
zhangsan=zhangsan,manager
lisi=lisi,guest
[roles]
# administrator 表示角色名称,* 表示这个角色拥有所有权限
administrator=*
manager=user:*,department:*
guest=user:query,department:query

Среди них у каждого пользователя может быть несколько ролей, разделенных запятыми. Каждая роль может иметь несколько разрешений, опять же разделенных запятыми.

3.3 Кодирование

public class ShiroTest {
    @Test
    public void test() {
        // 读取 shiro.ini 文件内容
        Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
        SecurityManager securityManager = factory.getInstance();
        SecurityUtils.setSecurityManager(securityManager);
        Subject currentUser = SecurityUtils.getSubject();
        Session session = currentUser.getSession();
        session.setAttribute("someKey", "aValue");
        String value = (String) session.getAttribute("someKey");
        if (value.equals("aValue")) {
            System.out.println("someKey 的值:" + value);
        }
        // 登陆
        UsernamePasswordToken token = new UsernamePasswordToken("zhangsan", "zhangsan");
        token.setRememberMe(true);
        try {
            currentUser.login(token);
        } catch (UnknownAccountException uae) {
            System.out.println("用户名不存在:" + token.getPrincipal());
        } catch (IncorrectCredentialsException ice) {
            System.out.println("账户密码 " + token.getPrincipal()  + " 不正确!");
        } catch (LockedAccountException lae) {
            System.out.println("用户名 " + token.getPrincipal() + " 被锁定 !");
        }
        
        // 认证成功后
        if (currentUser.isAuthenticated()) {
            
            System.out.println("用户 " + currentUser.getPrincipal() + " 登陆成功!");
            
            //测试角色
            System.out.println("是否拥有 manager 角色:" + currentUser.hasRole("manager"));
            
            //测试权限
            System.out.println("是否拥有 user:create 权限" + currentUser.isPermitted("user:create"));
            
            //退出
            currentUser.logout();
        }
    }
}

распечатать результат:

someKey 的值:aValue
用户 zhangsan 登陆成功!
是否拥有 manager 角色:true
是否拥有 user:create 权限true

в проектевнутренний код, мы можем определить статус аутентификации и информацию об авторизации текущего вошедшего в систему пользователя через объект Subject.Ниже приведен список методов, связанных с аутентификацией и авторизацией объекта Subject:

image

Если разработчик не хочет использовать код для проверки авторизации пользователя и других операций, то вместо него можно использовать аннотации.

Прежде чем использовать аннотации Широ, убедитесь, что в проект добавлены соответствующие пакеты jar, поддерживающие функции АОП. Общие аннотации следующие:

@RequiresRoles( "manager" )      # 角色校验
public String save() {
    //...
}
@RequiresPermissions("user:manage")  # 权限检验
public String delete() {
    //...
}

Когда клиент отправляет запрос, система будет использовать AOP для создания прокси-класса цели запроса, чтобы проанализировать аннотацию, а затем определить, есть ли у текущего запрашивающего пользователя разрешение на доступ.

в проектеинтерфейсный код, при использовании шаблона JSP мы можем использовать теги, предоставленные Широ, для управления отображением элементов страницы.

Например:

<%@ taglib prefix="shiro" uri=http://shiro.apache.org/tags %>
<html>
<body>
    <shiro:hasPermission name="user:manage">
        <a href="manageUsers.jsp">
            点击进入管理界面
        </a>
    </shiro:hasPermission>
    <shiro:lacksPermission name="user:manage">
        没有管理权限
    </shiro:lacksPermission>
</body>
</html>

Среди них user: management соответствует настройкам ниже [Roles] в файле Shiro.ini.

4. Пользовательский мир

Вышеприведенная программа использует IniRealm, поставляемый с Shiro, и IniRealm считывает информацию о пользователе из файла конфигурации ini.В большинстве случаев информацию о пользователе необходимо считывать из системной базы данных, поэтому вам необходимо настроить область.

Широ предоставляет нам следующие Realms:

image

Среди них самым основным является интерфейс Realm, CachingRealm отвечает за обработку кеша, AuthenticationRealm отвечает за аутентификацию, AuthorizingRealm отвечает за авторизацию, обычно наследование пользовательской областиAuthorizingRealm.

Пользовательский мир:

public class CustomRealm extends AuthorizingRealm {
    /**
     * 认证
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        // 从 token 中获取用户身份信息
        String username = (String) token.getPrincipal();
        // 通过 username 从数据库中查询
    
        // 如果查询不到则返回 null
        if(!username.equals("zhangsan")){//这里模拟查询不到
            return null;
        }
        
        //获取从数据库查询出来的用户密码 
        String dbPassword = "zhangsan";//这里使用静态数据模拟
        
        //返回认证信息由父类 AuthenticatingRealm 进行认证
        SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(username, dbPassword, getName());
        return simpleAuthenticationInfo;
    }
    
    /**
     * 授权
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        // 获取身份信息
        String username = (String) principals.getPrimaryPrincipal();
        // 根据身份信息从数据库中查询权限数据
        // 这里使用静态数据模拟
        List<String> permissions = new ArrayList<String>();
        permissions.add("user:*");
        permissions.add("department:*");
        
        // 将权限信息封闭为AuthorizationInfo
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        // 模拟数据,添加 manager 角色
        simpleAuthorizationInfo.addRole("manager");
        
        for(String permission:permissions){
            simpleAuthorizationInfo.addStringPermission(permission);
        }
        
        return simpleAuthorizationInfo;
    }
}

Создайте файл shiro-realm.ini в каталоге src/main/resources со следующим содержимым:

[main]
#自定义 realm
customRealm=com.light.shiroTest.realm.CustomRealm
#将realm设置到securityManager
securityManager.realms=$customRealm

В тестовом классе измените shiro.ini на shiro-realm.ini и выполните, результаты следующие:

someKey 的值:aValue
用户 zhangsan 登陆成功!
是否拥有 manager 角色:true
是否拥有 user:create 权限true

5. Интеграция с Spring

Из-за разных идей дизайна проекта настройки при интеграции фреймворка Shiro также будут разными, поэтому ниже перечислены только некоторые общие коды.

5.1 Добавьте зависимости

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

5.2 Файлы конфигурации

веб.xml:

<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.xml:

<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
    <!-- 必须设置 -->
    <property name="securityManager" ref="securityManager"/>
    <!-- 3 个 url 属性为可选设置 -->
    <property name="loginUrl" value="/login.jsp"/>
    <property name="successUrl" value="/home.jsp"/>
    <property name="unauthorizedUrl" value="/unauthorized.jsp"/>
    <property name="filterChainDefinitions">
        <value>
            <!-- 对静态资源设置匿名访问 -->
            /resources/** = anon
            /login = anon
            <!-- /** = authc 所有url都必须认证通过才可以访问-->
            /** = authc
        </value>
    </property>
</bean>
<!-- 安全管理器 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
    <property name="realm" ref="customRealm" />
</bean>
<!-- 自定义 realm -->
<bean id="customRealm" class="com.light.ac.web.realm.CustomRealm"></bean>
<!-- 保证实现了Shiro内部lifecycle函数的bean执行 -->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />

Среди них имя shiroFilter в application-shiro.xml соответствует имени shiroFilter в файле web.xml и должно быть непротиворечивым.

anon и authc соответствуют упомянутым выше фильтрам.

Класс CustomRealm:

public class CustomRealm extends AuthorizingRealm {
    @Autowired
    private UserService userService;
    
    @Autowired
    private PermissionService permissionService;
    
    /**
     * 认证
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        // 获取用户名
        String userName = (String) token.getPrincipal();
        // 通过用户名获取用户对象
        User user = this.userService.findUserByUserName(userName);
        
        if (user == null) {
            return null;
        }
        
        // 通过 userId 获取该用户拥有的所有权限,返回值根据自己需求编写,并非固定值。
        Map<String,List<Permission>> permissionMap = this.permissionService.getPermissionMapByUserId(user.getId());
        
        // (目录+菜单,分层级,用于前端 jsp 遍历)
        user.setMenuList(permissionMap.get("menuList"));
        // (目录+菜单+按钮,用于后端权限判断)
        user.setPermissionList(permissionMap.get("permissionList"));
        
        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user,user.getPassword(),this.getName());
        
        return info;
    }
    
    /**
     * 授权
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        User user = (User) principals.getPrimaryPrincipal();
        
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        
        // (目录+菜单+按钮,用于后端权限判断)
        List<Permission> permissionList = user.getPermissionList();
        
        for (Permission permission : permissionList) {
            if (StringUtil.isNotEmpty(permission.getCode())) {
                info.addStringPermission(permission.getCode());
            }
        }
        
        return info;
    }
}

Конкретный код можно загрузить и просмотреть по адресу исходного кода, указанному ниже.

6. Загрузка исходного кода

authority-control-shiro

7. Ссылки