Shiro入门学习二

来源:互联网 发布:安卓 记账软件 编辑:程序博客网 时间:2024/05/19 12:40

身份验证简介

在 shiro 中,用户需要提供 principals (身份)和 credentials(证明)给 shiro,从而应用能验证用户身份:

principals:身份,即主体的标识属性,可以是任何东西,如用户名、邮箱等,唯一即可。一个主体可以有多个 principals,但只有一个 Primary principals,一般是邮箱 / 手机号。

credentials:证明 / 凭证,即只有主体知道的安全值,如密码 / 数字证书等。

最常见的 principals 和 credentials 组合就是用户名 / 密码了。

身份认证流程
身份认证流程

  1. 首先调用 Subject.login(token) 进行登录,其会自动委托给 Security Manager的login方法,调用之前必须通过 SecurityUtils.setSecurityManager() 设置;
  2. SecurityManager 负责真正的身份验证逻辑;它会委托给 Authenticator 进行身份验证;
  3. Authenticator 才是真正的身份验证者,Shiro API 中核心的身份认证入口点,此处可以自定义插入自己的实现;
  4. Authenticator 可能会委托给相应的 AuthenticationStrategy 进行多 Realm 身份验证,默认 ModularRealmAuthenticator 会调用 AuthenticationStrategy 进行多 Realm 身份验证;
  5. Authenticator 会把相应的 token 传入 Realm,从 Realm 获取身份验证信息,如果没有返回 / 抛出异常表示身份验证失败了。此处可以配置多个 Realm,将按照相应的顺序及策略进行访问。

Realm

什么是Realm

Shiro从Realm获取安全数据(如用户、角色、权限),SecurityManager要验证用户身份,它需要从Realm获取相应的用户进行比较以确定用户身份是否合法,也需要从Realm获取角色/权限,可以将Realm看成DataSource,即安全数据源。Realm可以配置单个或多个。

单Realm配置

自定义Realm实现

package com.shiro.realm;public class MyRealm1 implements Realm{    public AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {        String username = (String) token.getPrincipal();    //获取用户名        String password = new String((char[]) token.getCredentials());  //获取密码        //这里是模拟数据,实际开发中需要从数据库中查询        if(!"zhang".equals(username)){            throw new UnknownAccountException();        }        if(!"123".equals(password)){            throw new IncorrectCredentialsException();        }        //如果身份验证成功,返回一个AuthenticationInfo实现        return new SimpleAuthenticationInfo(username,password,getName());    }    public boolean supports(AuthenticationToken token) {        return token instanceof UsernamePasswordToken; //仅支持UsernamePasswordToken类型的Token    }    public String getName() {        //设置Realm名称        return "myRealm1";    }}

ini配置文件指定自定义Realm:

[main]#声明自定义的RealmmyRealm1=com.shiro.realm.MyRealm1#指定SecurityManager的realms实现securityManager.realms=$myRealm1

多Realm配置

ini配置文件配置

[main]#声明自定义的RealmmyRealm1=com.shiro.realm.MyRealm1myRealm2=com.shiro.realm.MyRealm2#指定SecurityManager的realms实现securityManager.realms=$myRealm1,$myRealm2

securityManager会按照realms指定的顺序进行身份认证,如果删除“securityManager.realms=$myRealm1,$myRealm2”,那么SecurityManager会按照Realm声明的顺序执行,当显示的指定realms时,未被指定的realm不会被执行。

shiro默认提供的Realm

这里写图片描述

以后一般继承AuthorizingRealm即可,它继承了AuthenticatingRealm(身份验证),间接继承了CachingRealm(带有缓存实现)。其中主要默认实现如下:

  • org.apache.shiro.realm.text.IniRealm:[users] 部分指定用户名 / 密码及其角色;[roles]部分指定角色即权限信息;
  • org.apache.shiro.realm.text.PropertiesRealm:user.username=password,role1,role2 指定用户名 /密码及其角色;role.role1=permission1,permission2 指定角色及权限信息;
  • org.apache.shiro.realm.jdbc.JdbcRealm:通过 sql 查询相应的信息,如 “select password from users where username = ?” 获取用户密码,“select password, password_salt from users where username = ?” 获取用户密码及盐;“select role_name from user_roles where username = ?” 获取用户角色;“select permission from roles_permissions where role_name = ?”获取角色对应的权限信息;也可以调用相应的 api 进行自定义 sql;

JDBC Realm 使用

在第一节中的maven配置中添加如下依赖

        <dependency>            <groupId>mysql</groupId>            <artifactId>mysql-connector-java</artifactId>            <version>5.1.25</version>        </dependency>        <dependency>            <groupId>com.alibaba</groupId>            <artifactId>druid</artifactId>            <version>0.2.23</version>        </dependency>

在数据库shiro下建三张表:users、users_roles、roles_permissions;

drop database if exists shiro;create database shiro;use shiro;create table users (  id bigint auto_increment,  username varchar(100),  password varchar(100),  password_salt varchar(100),  constraint pk_users primary key(id)) charset=utf8 ENGINE=InnoDB;create unique index idx_users_username on users(username);create table user_roles(  id bigint auto_increment,  username varchar(100),  role_name varchar(100),  constraint pk_user_roles primary key(id)) charset=utf8 ENGINE=InnoDB;create unique index idx_user_roles on user_roles(username, role_name);create table roles_permissions(  id bigint auto_increment,  role_name varchar(100),  permission varchar(100),  constraint pk_roles_permissions primary key(id)) charset=utf8 ENGINE=InnoDB;create unique index idx_roles_permissions on roles_permissions(role_name, permission);insert into users(username,password)values('zhang','123');

ini配置

jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealmdataSource=com.alibaba.druid.pool.DruidDataSourcedataSource.driverClassName=com.mysql.jdbc.DriverdataSource.url=jdbc:mysql://localhost:3306/shirodataSource.username=rootdataSource.password=jdbcRealm.dataSource=$dataSourcesecurityManager.realms=$jdbcRealm

Authenticator及AuthenticationStrategy

Authenticator 的职责是验证用户帐号,是 Shiro API 中身份验证核心的入口点:

public AuthenticationInfo authenticate(AuthenticationToken authenticationToken)            throws AuthenticationException

如果验证成功,将返回 AuthenticationInfo 验证信息;此信息中包含了身份及凭证;如果验证失败将抛出相应的 AuthenticationException 实现。
SecurityManager接口继承了Authenticator, Authorizer, SessionManager
Autenticator继承树

上述介绍了多Realm的使用,但遗留下了一个问题:多Realm验证怎样才算验证成功?一个Reaml验证成功即可,还是全部验证成功方可。shiro提供了AuthenticationStrategy(验证策略),Authenticator的另外一个ModularRealmAuthenticator实现的doAuthenticate方法会进行判断单Realm或多Realm验证,当多Realm验证时,验证规则通过AuthenticationStrategy接口指定,默认提供的实现:

  • FirstSuccessfulStrategy:只要有一个 Realm 验证成功即可,只返回第一个 Realm身份验证成功的认证信息,其他的忽略;
  • AtLeastOneSuccessfulStrategy:只要有一个 Realm 验证成功即可,和FirstSuccessfulStrategy 不同,返回所有 Realm 身份验证成功的认证信息;
  • AllSuccessfulStrategy:所有 Realm 验证成功才算成功,且返回所有 Realm身份验证成功的认证信息,如果有一个失败就失败了。

ModularRealmAuthenticator 默认使用AtLeastOneSuccessfulStrategy 策略。

ini配置如下:

[main]#指定securityManager的authenticator实现authenticator=org.apache.shiro.authc.pam.ModularRealmAuthenticatorsecurityManager.authenticator=$authenticator#指定securityManager.authenticator的authenticationStrategyallSuccessfulStrategy=org.apache.shiro.authc.pam.AllSuccessfulStrategysecurityManager.authenticator.authenticationStrategy=$allSuccessfulStrategymyRealm1=com.shiro.realm.MyRealm1myRealm2=com.shiro.realm.MyRealm2myRealm3=com.shiro.realm.MyRealm3securityManager.realms=$myRealm1,$myRealm3

验证策略可以自定义实现,只要继承org.apache.shiro.authc.pam.AbstractAuthenticationStrategy,重写其中的方法:

//在所有Realm验证之前调用AuthenticationInfo beforeAllAttempts(Collection<? extends Realm> realms, AuthenticationToken token) throws AuthenticationException;//在每个Realm之前调用AuthenticationInfo beforeAttempt(Realm realm, AuthenticationToken token, AuthenticationInfo aggregate) throws AuthenticationException;//在每个Realm之后调用AuthenticationInfo afterAttempt(Realm realm, AuthenticationToken token, AuthenticationInfo singleRealmInfo, AuthenticationInfo aggregateInfo, Throwable t) throws AuthenticationException;//在所有Realm之后调用AuthenticationInfo afterAllAttempts(AuthenticationToken token, AuthenticationInfo aggregate) throws AuthenticationException;
原创粉丝点击