Java基础、中级、高级、架构面试资料

Shiro 的 Realm

JAVA herman 2931浏览 0评论
公告:“业余草”微信公众号提供免费CSDN下载服务(只下Java资源),关注业余草微信公众号,添加作者微信:xttblog2,发送下载链接帮助你免费下载!
本博客日IP超过2000,PV 3000 左右,急需赞助商。
极客时间所有课程通过我的二维码购买后返现24元微信红包,请加博主新的微信号:xttblog2,之前的微信号好友位已满,备注:返现
受密码保护的文章请关注“业余草”公众号,回复关键字“0”获得密码
所有面试题(java、前端、数据库、springboot等)一网打尽,请关注文末小程序
视频教程免费领
腾讯云】1核2G5M轻量应用服务器50元首年,高性价比,助您轻松上云

在认证、授权内部实现机制中都有提到,最终处理都将交给Realm进行处理。因为在Shiro中,最终是通过Realm来获取应用程序中的用户、角色及权限信息的。通常情况下,在Realm中会直接从我们的数据源中获取Shiro需要的验证信息。可以说,Realm是专用于安全框架的DAO。因此我把 Realm 专门抽离出来作为单独的一章进行讲解。

Realm

域,Shiro从从Realm获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm得到用户相应的角色/权限进行验证用户是否能进行操作;可以把Realm看成DataSource,即安全数据源。如我们之前的ini配置方式将使用org.apache.shiro.realm.text.IniRealm。

Realm 的接口

String getName(); //返回一个唯一的Realm名字  
boolean supports(AuthenticationToken token); //判断此Realm是否支持此Token  
AuthenticationInfo getAuthenticationInfo(AuthenticationToken token)  throws AuthenticationException;  //根据Token获取认证信息 

自定义 Realm 的实现

新建 XttblogRealm 并实现 Realm接口,代码如下:

package com.xttblog.realm;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.realm.Realm;

public class XttblogRealm implements Realm{
	
	@Override  
    public String getName() {  
        return "XttblogRealm";  
    }  
	
    @Override  
    public boolean supports(AuthenticationToken token) {  
        //仅支持UsernamePasswordToken类型的Token  
        return token instanceof UsernamePasswordToken;   
    }  
    
    @Override  
    public AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {  
        String username = (String)token.getPrincipal();  //得到用户名  
        String password = new String((char[])token.getCredentials()); //得到密码  
        if(!"xttblog".equals(username)) {  
            throw new UnknownAccountException(); //如果用户名错误  
        }  
        if(!"123".equals(password)) {  
            throw new IncorrectCredentialsException(); //如果密码错误  
        }  
        //如果身份认证验证成功,返回一个AuthenticationInfo实现;  
        return new SimpleAuthenticationInfo(username, password, getName());  
    }  
}

ini配置文件指定自定义Realm实现(shiro-realm.ini) 

#声明一个realm
xttblogRealm=com.xttblog.realm.XttblogRealm
#指定securityManager的realms实现
securityManager.realms=$xttblogRealm

通过$name来引入之前的realm定义

测试代码如下:

@Test
public void testXttblogRealm() {
	//1、获取SecurityManager工厂,此处使用Ini配置文件初始化SecurityManager
	Factory<org.apache.shiro.mgt.SecurityManager> factory =
			new IniSecurityManagerFactory("classpath:shiro-realm.ini");

	//2、得到SecurityManager实例 并绑定给SecurityUtils
	org.apache.shiro.mgt.SecurityManager securityManager = factory.getInstance();
	SecurityUtils.setSecurityManager(securityManager);

	//3、得到Subject及创建用户名/密码身份验证Token(即用户身份/凭证)
	Subject subject = SecurityUtils.getSubject();
	UsernamePasswordToken token = new UsernamePasswordToken("xttblog", "123");

	try {
		//4、登录,即身份验证
		subject.login(token);
	} catch (AuthenticationException e) {
		//5、身份验证失败
		e.printStackTrace();
	}

	Assert.assertEquals(true, subject.isAuthenticated()); //断言用户已经登录

	//6、退出
	subject.logout();
}

多Realm配置

shiro-multi-realm.ini 配置文件

#声明一个realm
xttblogRealm=com.xttblog.realm.XttblogRealm
codedqRealm=com.xttblog.realm.CodedqRealm
#指定securityManager的realms实现
securityManager.realms=$xttblogRealm,$codedqRealm

securityManager会按照realms指定的顺序进行身份认证。此处我们使用显示指定顺序的方式指定了Realm的顺序,如果删除“securityManager.realms=$xttblogRealm,$codedqRealm”,那么securityManager会按照realm声明的顺序进行使用(即无需设置realms属性,其会自动发现),当我们显示指定realm后,其他没有指定realm将被忽略,如“securityManager.realms=$xttblogRealm”,那么codedqRealm不会被自动设置进去。

Shiro默认提供的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 pom 中添加数据库及依赖

<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(用户名/密码)、user_roles(用户/角色)、roles_permissions(角色/权限),并添加一个用户记录,用户名/密码为xttblog/123。相关SQL如下:

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('xttblog','123');

配置 shiro-jdbc-realm.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

配置说明:

  • 变量名=全限定类名会自动创建一个类实例
  • 变量名.属性=值 自动调用相应的setter方法进行赋值
  • $变量名 引用之前的一个对象实例

测试用例代码:

@Test
public void testJDBCRealm() {
    //1、获取SecurityManager工厂,此处使用Ini配置文件初始化SecurityManager
    Factory<org.apache.shiro.mgt.SecurityManager> factory =
            new IniSecurityManagerFactory("classpath:shiro-jdbc-realm.ini");

    //2、得到SecurityManager实例 并绑定给SecurityUtils
    org.apache.shiro.mgt.SecurityManager securityManager = factory.getInstance();
    SecurityUtils.setSecurityManager(securityManager);

    //3、得到Subject及创建用户名/密码身份验证Token(即用户身份/凭证)
    Subject subject = SecurityUtils.getSubject();
    UsernamePasswordToken token = new UsernamePasswordToken("xttblog", "123");

    try {
        //4、登录,即身份验证
        subject.login(token);
    } catch (AuthenticationException e) {
        //5、身份验证失败
        e.printStackTrace();
    }

    Assert.assertEquals(true, subject.isAuthenticated()); //断言用户已经登录

    //6、退出
    subject.logout();
}

相关代码下载链接:http://pan.baidu.com/s/1jH6OmqU 密码:y3qp

业余草公众号

最后,欢迎关注我的个人微信公众号:业余草(yyucao)!可加作者微信号:xttblog2。备注:“1”,添加博主微信拉你进微信群。备注错误不会同意好友申请。再次感谢您的关注!后续有精彩内容会第一时间发给您!原创文章投稿请发送至532009913@qq.com邮箱。商务合作也可添加作者微信进行联系!

本文原文出处:业余草: » Shiro 的 Realm