Shiro入门学习二

身份验证简介

在 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]
#声明自定义的Realm
myRealm1=com.shiro.realm.MyRealm1
#指定SecurityManager的realms实现
securityManager.realms=$myRealm1

多Realm配置

ini配置文件配置

[main]
#声明自定义的Realm
myRealm1=com.shiro.realm.MyRealm1
myRealm2=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.JdbcRealm
dataSource=com.alibaba.druid.pool.DruidDataSource
dataSource.driverClassName=com.mysql.jdbc.Driver
dataSource.url=jdbc:mysql://localhost:3306/shiro
dataSource.username=root
dataSource.password=
jdbcRealm.dataSource=$dataSource
securityManager.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.ModularRealmAuthenticator
securityManager.authenticator=$authenticator
#指定securityManager.authenticator的authenticationStrategy
allSuccessfulStrategy=org.apache.shiro.authc.pam.AllSuccessfulStrategy
securityManager.authenticator.authenticationStrategy=$allSuccessfulStrategy
myRealm1=com.shiro.realm.MyRealm1
myRealm2=com.shiro.realm.MyRealm2
myRealm3=com.shiro.realm.MyRealm3
securityManager.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;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值