Shiro 简介
什么是Shiro
Apache Shiro是一个强大且易用的Java安全框架,执行身份验证,授权、密码学和会话管理。
Authentication:身份认证/登录,验证用户是不是拥有相应的身份;
Authorization:授权,即验证权限,验证某个已认证的用户是否拥有某个权限;即判断用户是否能做事情,常见的如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限。
Session Management:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通的JavaSE环境的,也可以是Web环境的。
Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;
Web Support:Web支持,可以非常容易的集成到Web环境中
Caching:缓存,比如用户登录后,其用户信息,拥有的角色/权限不必每次去查,提高效率。
Concurrency:Shiro支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去;
Testing:提供测试支持
Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问
Rember Me:记住我,这是非常常见的功能,即一次登录后,下次再来的话不用登录了。
主要功能
身份验证、授权、加密、会话
身份认证
流程如下:
1、首先调用 Subject.login(token)进行登录,其会自动委托给 Security Manager,调用之前必
须通过 SecurityUtils. setSecurityManager()设置;
2、SecurityManager 负责真正的身份验证逻辑;它会委托给 Authenticator 进行身份验证;
3、Authenticator 才是真正的身份验证者,Shiro API 中核心的身份认证入口点,此处可以自
定义插入自己的实现;
4、Authenticator 可能会委托给相应的 AuthenticationStrategy 进行多 Realm 身份验证,默认
ModularRealmAuthenticator 会调用 AuthenticationStrategy 进行多 Realm 身份验证;
5、Authenticator 会把相应的 token 传入 Realm,从 Realm 获取身份验证信息,如果没有返
回/抛出异常表示身份验证失败了。此处可以配置多个 Realm,将按照相应的顺序及策略进
行访问。
Shiro 第一个小程序
- 添加jar包
<!---shiro 架包 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
</dependency>
<!---日记jar包 使用shiro需要使用这jar包 -->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</dependency>
<!---数据库 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</dependency>
<!-- 日志 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</dependency>
- 配置核心文件
Shiro 使用的ini文件当做配置文件
shiro.ini
#定义用户信息,用户名,或者密码
[users]
admin=123
tom=456
测试
public static void main(String[] args) {
//第一步 获取Security Manager ,指定配置文件初始化
IniSecurityManagerFactory factory= new IniSecurityManagerFactory("classpath:shiro01/shiro01.ini");
SecurityManager securityManager = factory.getInstance();
//第二步 对SecurityManager进行封装,委托给SecurityUtils
SecurityUtils.setSecurityManager(securityManager);
//第三步获取用户主体 Subject
org.apache.shiro.subject.Subject subject=SecurityUtils.getSubject();
//第四步登录
UsernamePasswordToken token=new UsernamePasswordToken("admin","123");
//第五步 身份认证 认证成功返回true,认证失败抛出异常
try {
//认证前
// System.out.println("认证之前-是否认证:"+subject.isAuthenticated());
subject.login(token);
// System.out.println("认证之后-是否认证:"+subject.isAuthenticated());
// System.out.println("调用logout");
// subject.logout();
// System.out.println("是否认证:"+subject.isAuthenticated());
} catch (UnknownAccountException e) {
System.out.println("账户异常:"+e.getMessage());
}catch (IncorrectCredentialsException e)
{
System.out.println("密码异常:"+e.getMessage());
}
}
Shiro 默认配置
在ini核心配置文件中有如下默认配置,SecurityManager、Authenticator 、Authentication、Realm
#定义用户信息,用户名,或者密码
[users]
admin=123
tom=456
#以下默认可以省略#
#默认的SecurityManager
##相当于spring容器中定义的bean securityManager 为id org.apache.shiro.mgt.DefaultSecurityManager 是对应的bean 实现类
securityManager=org.apache.shiro.mgt.DefaultSecurityManager
# 默认认证器
authenticator=org.apache.shiro.authc.pam.ModularRealmAuthenticator
#将认证器添加到SecurityManager中
securityManager.authentication=$authenticator
# 默认的认证策略
authenticationStrategy=org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy
#将认证策略添加到认证器中,相当于注入操作,要以$开头
authenticator.authenticationStrategy=$authenticationStrategy
#默认的Realm
iniRealm=org.apache.shiro.realm.text.IniRealm
#将Realm添加到SecurityManager中
securitymanager.realms=$iniRealm
Realm 介绍
Realm :安全数据源,也就是数据放哪里。
默认有三种:
- iniRealm :数据放到ini文件里面
- JdbcRealm:数据放到数据库里面
- PropertiesRealm 数据放到Properties文件里面
iniRealm 使用:
见第一个shiro例子
JdbcRealm 使用: - 导包
- 建表
drop database if exists shiro;
create database shiro charset utf8 ;
use shiro;
create table t_user
(
id int primary key auto_increment,
login_name varchar(200) not null unique,
password varchar(200)
)engine=Innodb charset utf8;
insert into t_user(login_name,password)values(\'admin\',\'111\');
insert into t_user(login_name,password)values(\'tom\',\'222\');
- 工具类
public class ShiroUtils {
public static Subject getSubject(String iniPath) {
//第一步 获取Security Manager ,指定配置文件初始化
IniSecurityManagerFactory factory = new IniSecurityManagerFactory(iniPath);
org.apache.shiro.mgt.SecurityManager securityManager = factory.getInstance();
// 第二步 对SecurityManager进行封装委托给SecurityUtils
SecurityUtils.setSecurityManager(securityManager);
//第三步获取用户主体 Subject
org.apache.shiro.subject.Subject subject = SecurityUtils.getSubject();
return subject;
}
}
- 日志文件
log4j.rootLogger=info,hello
log4j.appender.hello=org.apache.log4j.ConsoleAppender
log4j.appender.hello.layout=org.apache.log4j.SimpleLayout
- ini配置文件
#配置数据源
dataSource=com.alibaba.druid.pool.DruidDataSource
dataSource.driverClassName=com.mysql.jdbc.Driver
dataSource.url=jdbc:mysql://localhost:3306/shiro?useUnicde=false&characterEncoding=utf-8
dataSource.username=root
dataSource.password=123456
# 使用JdbcRealm
jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealm
#注入数据源
jdbcRealm.dataSource=$dataSource
#默认sql语句
# protected static final java.lang.String DEFAULT_AUTHENTICATION_QUERY = "select password from users where username = ?";
# protected String authenticationQuery = DEFAULT_AUTHENTICATION_QUERY;
# public void setAuthenticationQuery(String authenticationQuery) { this.authenticationQuery = authenticationQuery; }
#重写认证sql语句
jdbcRealm.authenticationQuery=select password from t_user where login_name=?
# 将JdbcRealm 添加到SecurityManager
#securityManager 是默认名 securityManager=org.apache.shiro.mgt.DefaultSecurityManager
securityManager.realms=$jdbcRealm
- 测试
public static void main(String[] args) {
Subject subject= ShiroUtils.getSubject("classpath:shiro02/shiro.ini");
UsernamePasswordToken token=new UsernamePasswordToken("admin","111");
try {
//认证前
// System.out.println("认证之前-是否认证:"+subject.isAuthenticated());
subject.login(token);
// System.out.println("认证之后-是否认证:"+subject.isAuthenticated());
// System.out.println("调用logout");
// subject.logout();
System.out.println("是否认证:"+subject.isAuthenticated());
} catch (UnknownAccountException e) {
System.out.println("账户异常:"+e.getMessage());
}catch (IncorrectCredentialsException e)
{
System.out.println("密码异常:"+e.getMessage());
}catch (AuthenticationException e){
System.out.println(e.getMessage());
}
}
自定义Realm
如果使用的是QQ、微信等登陆方式,则需要自定义Realm
步骤
- 定义一个类,继承 AuthorizingRealm
public class PropertiesRealm extends AuthorizingRealm {
private String path;
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
/*
*
* 授权
* */
@Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
return null;
}
/*
* 身份的验证
* */
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)
throws AuthenticationException {
//获取token中的用户名
UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
String username = usernamePasswordToken.getUsername();
//查找数据源
Properties p = new Properties();
try {
if (path.startsWith("classpath:")) {
path = path.substring(10);
p.load(PropertiesRealm.class.getClassLoader().getResourceAsStream(path));
} else if (path.startsWith("file:")) {
p.load(new FileInputStream(path));
}
} catch (IOException ex) {
ex.printStackTrace();
}
String password=p.getProperty("user."+username);
if(password!=null)
{
AuthenticationInfo authenticationInfo=new SimpleAuthenticationInfo(
username,//用户名
password,//数据源中真的真是密码
getName()//Reaml 名称
);
return authenticationInfo;
}
return null;
}
}
- 配置文件
# 使用自定义Reaml
propertiesRealm= shiro02.realm.PropertiesRealm
propertiesRealm.path=classpath:shiro02/user.properties
# 将JdbcRealm 添加到SecurityManager
#securityManager 是默认名 securityManager=org.apache.shiro.mgt.DefaultSecurityManager
securityManager.realms=$propertiesRealm
*属性文件
user.admin=11
user.tom=666