编辑推荐: |
文章主要介绍一个安全框架Shiro,并且详细介绍了Shiro架构图、环境搭建、认证实现以及密码加密实现方案等相关内容,希望对您能有所帮助。
本文来自于cnblogs,由火龙果软件Luca编辑、推荐。 |
|
一、Shiro简介
Apache Shiro是一个强大易用的Java安全框架,提供了认证、授权、加密和会话管理等功能。
对于任何一个应用程序,Shiro都可以提供全面的安全管理服务。其不仅可以用在JavaSE环境,也可以用在JavaEE环境。
二、Shiro架构图
1.从外部来看Shiro,即从应用程序角度来观察如何使用Shiro完成工作。如下图:
2.从Shiro内部看Shiro的架构,如下图所示:
Subject(org.apache.shiro.subject.Subject)当前与软 件进行交互的实体(用户,第三方服务,cron
job,等 等)的安全特定“视图”
SecurityManager:SecurityManager 是
Shiro 架构的心脏。它基本上是一个“保护伞”对象,协调其管理的组件以确保它们能够一起顺利的工作类似于SpringMVC中的入口
servlet
Realms:域 Realms 在 Shiro 和你的应用程序的安全数据之间担当 “桥梁”或“连接器”。当它实际上与安全相关的数据如用
来执行身份验证(登录)及授权(访问控制)的用户帐户交互时, Shiro从一个或多个为应用程序配置的Real中寻找许多这样的东西
Shiro 的环境搭建
使用 shiro 实现登陆的操作
第一步 导包
第二步:书写 shiro.ini 文件
第三步:书写测试代码
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFact
ory; import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory; import
org.apache.shiro.mgt.SecurityManager;
public class TestA {
public static void main(String[] args) {
//[1]解析shiro.ini文件
Factory<SecurityManager> factory =new
IniSecurityManagerFactory("classpath:shiro.ini
");
//[2]通过SecurityManager工厂获得 SecurityManager实例
SecurityManager securityManager = factory.getInstance();
//[3]把SecurityManager对象设置到运行环境中
SecurityUtils.setSecurityManager(securityManag
er);
//[4]通过SecurityUtils获得主体subject Subject subject
= SecurityUtils.getSubject();
//[5]书写自己输入的账号和密码---相当于用户自 己输入的账号和密码
//我们拿着自己书写用户名密码去和shiro.ini 文 件中的账号密码比较 UsernamePasswordToken
token =new UsernamePasswordToken("sxt","root");
//[6]进行身份的验证 subject.login(token);
//[7]通过方法判断是否登录成功
if(subject.isAuthenticated()){ System.out.println("登录成功");
}else { System.out.println("登录失败");
}
}
} |
Shiro 验证时异常分析
DisabledAccountException
账户失效异常
ConcurrentAccessException
竞争次数过多
ExcessiveAttemptsException
尝试次数过多
UnknownAccountException
用户名不正确
IncorrectCredentialsException
凭证(密码)不正确
ExpiredCredentialsException
凭证过期
Shiro--认证流程
三、Shiro涉及常见名词
四、Shiro配置文件详解
shiro.ini文件放在classpath下,shiro会自动查找。其中格式是key/value键值对配置。INI配置文件一般适用于用户少且不需要在运行时动态创建的情景下使用。
ini文件中主要配置有四大类:main,users,roles,urls
示例:
1、[main]
main主要配置shiro的一些对象,例如securityauthenticator,authcStrategy
等等,例如:
2、[users]
[users]允许你配置一组静态的用户,包含用户名,密码,角色,一个用户
可以有多个角色,可以配置多个角色,例如:
3、[roles]
[roles]将角色和权限关联起来,格式为:角色名=权限字符串1,权限字符
串2…..,例如:
4、[urls]
这部分配置主要在web应用中,格式为:url=拦截器[参数],拦截器[参数]……,例如
五、认证实现
认证:验证用户是否合法
在 shiro 中,用户需要提供principals (身份)和credentials(凭证)
给shiro,从而实现对用户身份的验证。
5.1.principals(用户名)
身份,即主体的标识属性,可以是任何东西,如用户名、邮箱等,唯一即可。
例如:用户名/邮箱/手机号等。
5.2.credentials(密码)
凭证,即只有主体知道的安全值,如密码/数字证书等。
最常见的principals和credentials组合就是用户名/密码了。
5.3 实现步骤
5.3.1 导入jar包
5.3.2 从源码的示例项目quickstart中拷贝shiro.ini放到src下,并配置
5.3.3 编写代码
package com.bjsxt.test;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
import org.junit.Test;
//实现简单认证
public class AuthenticationTest {
@Test
public void testAuthentication(){
//1.构建SecurityManager工厂
IniSecurityManagerFactory securityManagerFactory
= new IniSecurityManagerFactory("classpath:shiro.ini");
//2.通过securityManagerFactory工厂获取SecurityManager实例
SecurityManager securityManager = securityManagerFactory.getInstance();
//3.将securityManager设置到运行环境当中
SecurityUtils.setSecurityManager(securityManager);
//4.获取subject实例
Subject subject = SecurityUtils.getSubject();
//5.创建用户名密码验证令牌Token
UsernamePasswordToken token = new UsernamePasswordToken
("victor","123456");
//6.进行身份验证
subject.login(token);
//7.判断是否认证通过
System.out.println(subject.isAuthenticated());
}
} |
shiro.ini
六、JDBCRealm
Shiro默认使用自带的IniRealm,IniRealm从ini配置文件中读取用户的信息。
大部分情况下需要从系统的数据库中读取用户信息,所以需要使用JDBCRealm或自定义Realm。
需求:使用JDBCRealm提供数据源,从而实现认证
实现步骤:
6.1建users表(表名、字段对应上)
6.2添加jar包(数据库驱动、数据库连接池、beanutils等)
6.3编写shiro.ini
6.4编写测试代码
AuthenticationTest.java
package com.bjsxt.test;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
import org.junit.Test;
//实现简单认证
public class AuthenticationTest {
@Test
public void testAuthentication(){
//1.构建SecurityManager工厂
IniSecurityManagerFactory securityManagerFactory
= new IniSecurityManagerFactory("classpath:shiro.ini");
//2.通过securityManagerFactory工厂获取SecurityManager实例
SecurityManager securityManager = securityManagerFactory.getInstance();
//3.将securityManager设置到运行环境当中
SecurityUtils.setSecurityManager(securityManager);
//4.获取subject实例
Subject subject = SecurityUtils.getSubject();
//5.创建用户名密码验证令牌Token
UsernamePasswordToken token = new UsernamePasswordToken
("victor","123456");
//6.进行身份验证
subject.login(token);
//7.判断是否认证通过
System.out.println(subject.isAuthenticated());
}
} |
shiro.ini
[main]
#配置Realm
jdbcRealm = org.apache.shiro.realm.jdbc.JdbcRealm
#配置数据源
dataSource = com.mchange.v2.c3p0.ComboPooledDataSource
dataSource.driverClass = com.mysql.jdbc.Driver
dataSource.jdbcUrl = jdbc:mysql:///test
dataSource.user = root
dataSource.password = victor
jdbcRealm.dataSource = $dataSource
#将Realm注入给SecurityManager
securityManager.realm = $jdbcRealm |
认证策略
规定了如果有多个数据源的时候应该如何操作
AtLeastOneSuccessfulStrategy
如果一个(或更多)Realm 验证成功,则整体的尝试被认为是成功的。
如果没有一个验证成功,
则整体尝试失败 类似于 java 中的 &
FirstSuccessfulStrategy
只有第一个成功地验证的 Realm 返回的信息将被使用。所有进一步的
Realm 将被忽略。如果没有一个验证成功,则整体尝试失败。
类似于 java 中的 &&
AllSucessfulStrategy
为了整体的尝试成功,所有配置的 Realm 必须验证成功。如果没有一个验
证成功,则整体尝试失败
package com.bjsxt.shiro1;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
public class TestA {
public static void main(String[] args) {
/*Realm*/
//[1]解析shiro.ini文件
Factory<SecurityManager> factory =new
IniSecurityManagerFactory("classpath:shiro-jdbc.ini");
//[2]通过SecurityManager工厂获得SecurityManager实例
SecurityManager securityManager = factory.getInstance();
//[3]把SecurityManager对象设置到运行环境中
SecurityUtils.setSecurityManager(securityManager);
//[4]通过SecurityUtils获得主体subject
Subject subject = SecurityUtils.getSubject();
//[5]书写自己输入的账号和密码---相当于用户自己输入的账号和密码
//我们拿着自己书写用户名密码去和shiro.ini 文件中的账号密码比较
UsernamePasswordToken token =new UsernamePasswordToken("root","123");
try {
//[6]进行身份的验证
subject.login(token);
//[7]通过方法判断是否登录成功
if(subject.isAuthenticated()){
System.out.println("登录成功");
}
} catch (IncorrectCredentialsException e) {
System.out.println("登录失败");
}catch (UnknownAccountException e){
System.out.println("用户名不正确");
}
}
} |
[main]
#获得数据源A
dataSou=com.mchange.v2.c3p0.ComboPooledDataSource
dataSou.driverClass=com.mysql.jdbc.Driver
dataSou.jdbcUrl=jdbc:mysql://127.0.0.1:3306/shiro
dataSou.user=root
dataSou.password=root
#配置了jdbcRealmA
jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealm
jdbcRealm.dataSource=$dataSou
#获得数据源B
dataSou1=com.mchange.v2.c3p0.ComboPooledDataSource
dataSou1.driverClass=com.mysql.jdbc.Driver
dataSou1.jdbcUrl=jdbc:mysql://127.0.0.1:3306/shiro1
dataSou1.user=root
dataSou1.password=root
#配置了jdbcRealmB
jdbcRealm1=org.apache.shiro.realm.jdbc.JdbcRealm
jdbcRealm1.dataSource=$dataSou1
#配置验证器
authenticationStrategy=org. apache .shiro.authc.pam.FirstSuccessfulStrategy
#设置securityManager中realm
securityManager.realms=$jdbcRealm,$jdbcRealm1
securityManager.authenticator.authenticationStrategy
=$authenticationStrategy |
七、如何自定义Realm
[1]为什么使用自定义 Realm
我们使用 JDBCRealm 的时候发现,shiro 的底层自己封装了数据库
表的名称和字段的名称,这样就造成了使用起来非常不方便
[2]解决方案
自定义 Realm
我们如果自己定义 realm 的话,可以实现这个接口
自定义Realm,可以注入给securityManager更加灵活的安全数据源(例如,JDBCRealm中表和字段都限定了)
通过实现Realm接口,或根据需求继承他的相应子类即可。
需求:使用自定义Realm提供数据源,从而实现认证
实现步骤:
6.1添加jar包
6.2编写自定义Realm
6.3编写shiro.ini
6.4编写测试类
package com.bjsxt.shiro2;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
public class UserRealm extends AuthorizingRealm
{
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo
(AuthenticationToken authenticationToken) throws
AuthenticationException {
//System.out.println(authenticationToken .getPrincipal());
try {
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager. getConnection
("jdbc:mysql://127.0.0.1:3306/shiro",
"root", "root");
PreparedStatement prepareStatement = conn.prepareStatement("select
pwd from admin where uname =? ");
prepareStatement.setObject (1,authenticationToken.getPrincipal());
ResultSet rs = prepareStatement.executeQuery();
while (rs.next()){
SimpleAuthenticationInfo info=new SimpleAuthenticationInfo
(authenticationToken .getPrincipal(),rs.getString("pwd"),"userRealm");
return info;
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo
(PrincipalCollection principalCollection) {
return null;
}
} |
package com.bjsxt.shiro2;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
public class TestB {
public static void main(String[] args) {
/*Realm*/
//[1]解析shiro.ini文件
Factory<SecurityManager> factory =new
IniSecurityManagerFactory("classpath:shiro-jdbc2.ini");
//[2]通过SecurityManager工厂获得SecurityManager实例
SecurityManager securityManager = factory.getInstance();
//[3]把SecurityManager对象设置到运行环境中
SecurityUtils.setSecurityManager(securityManager);
//[4]通过SecurityUtils获得主体subject
Subject subject = SecurityUtils.getSubject();
//[5]书写自己输入的账号和密码---相当于用户自己输入的账号和密码
//我们拿着自己书写用户名密码去和shiro.ini 文件中的账号密码比较
UsernamePasswordToken token =new UsernamePasswordToken("root","123");
try {
//[6]进行身份的验证
subject.login(token);
//[7]通过方法判断是否登录成功
if(subject.isAuthenticated()){
System.out.println("登录成功");
}
} catch (IncorrectCredentialsException e) {
System.out.println("登录失败");
}catch (UnknownAccountException e){
System.out.println("用户名不正确");
}
}
} |
[main]
#设置securityManager中realm
userRealm=com.bjsxt.shiro2.UserRealm
securityManager.realms=$userRealm |
【代码示例】
realms
package com.bjsxt.realms;
import java.net.ConnectException;
import java.security.interfaces.RSAKey;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.realm.AuthenticatingRealm;
import com.mysql.jdbc.Driver;
public class CustomRealm extends AuthenticatingRealm
{
private String principal;
private String credentials;
private ResultSet rs;
private Statement state;
private Connection conn;
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken
token) throws AuthenticationException {
//使用JDBC,从数据库获取数据
try {
//1.注册驱动
Driver driver = new Driver();
DriverManager.registerDriver(driver);
//2.获取连接对象
String url ="jdbc:mysql:///test";
String user = "root";
String password = "victor";
conn = DriverManager.getConnection(url , user
, password );
state = conn.createStatement();
//4.执行sql语句
String sql = "select userName,passwd from
starLogin";
rs = state.executeQuery(sql );
//5.处理结果集
while (rs.next()) {
principal = rs.getString("userName");
credentials = rs.getString("passwd");
}
} catch (SQLException e) {
e.printStackTrace();
}finally{
if(rs != null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(state != null){
try {
state.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
SimpleAuthenticationInfo simpleAuthenticationInfo
= new SimpleAuthenticationInfo(principal, credentials,
"customRealm");
return simpleAuthenticationInfo;
}
} |
AuthenticationTest
package com.bjsxt.test;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
import org.junit.Test;
//实现简单认证
public class AuthenticationTest {
@Test
public void testAuthentication(){
//1.构建SecurityManager工厂
IniSecurityManagerFactory securityManagerFactory
= new IniSecurityManagerFactory("classpath:shiro.ini");
//2.通过securityManagerFactory工厂获取SecurityManager实例
SecurityManager securityManager = securityManagerFactory.getInstance();
//3.将securityManager设置到运行环境当中
SecurityUtils.setSecurityManager(securityManager);
//4.获取subject实例
Subject subject = SecurityUtils.getSubject();
//5.创建用户名密码验证令牌Token
UsernamePasswordToken token = new UsernamePasswordToken("victor","123");
//6.进行身份验证
subject.login(token);
//7.判断是否认证通过
System.out.println(subject.isAuthenticated());
}
} |
Shrio.ini
[main]
#配置Realm
customRealm = com.bjsxt.realms.CustomRealm
#将Realm注入给SecurityManager
securityManager.realm = $customRealm |
八、密码加密实现方案
8.1几种常见加密算法比较
8.1.1对称加密算法(加密与解密密钥相同)
8.1.2非对称算法(加密密钥和解密密钥不同)
8.1.3 对称与非对称算法比较
8.1.4 散列算法比较‘
8.2 MD5加密、加盐与迭代
加盐:
使用MD5存在一个问题,相同的password生产的Hash值是相同的,如
果两个用户设置了相同的密码,那么数据库当就会存储相同的值,这样是极
不安全的。
加Salt可以一定程度上解决这一问题。所谓加Salt方法,就是加点
“佐料”。其基本想法是这样的:当用户首次提供密码时(通常是注册时),
由系统自动往这个密码里撒一些“佐料”,然后再散列。而当用户登录时,系统为用户提供的代码撒上同样的“佐料”,然后散列,再比较散列值,来确定密码是否正确。
加盐原理:
给原文加入随机数生成新的MD5值。
迭代:加密的次数
【代码示例】
md5Test
package com.bjsxt.test;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.junit.Test;
//MD5加密、加盐以及迭代
public class MD5Test {
@Test
public void testMD5(){
//md5加密
Md5Hash md5 = new Md5Hash("123456");
System.out.println(md5);
//加盐
md5 = new Md5Hash("123456", "bjsxt");
System.out.println(md5);
//迭代
md5 = new Md5Hash("123456", "bjsxt",
2);
System.out.println(md5);
}
} |
TestB.java
package com.bjsxt.shiro3;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
public class TestB {
public static void main(String[] args) {
/*Realm*/
//[1]解析shiro.ini文件
Factory<SecurityManager> factory =new
IniSecurityManagerFactory("classpath:shiro-jdbc3.ini");
//[2]通过SecurityManager工厂获得SecurityManager实例
SecurityManager securityManager = factory.getInstance();
//[3]把SecurityManager对象设置到运行环境中
SecurityUtils.setSecurityManager(securityManager);
//[4]通过SecurityUtils获得主体subject
Subject subject = SecurityUtils.getSubject();
//[5]书写自己输入的账号和密码---相当于用户自己输入的账号和密码
//我们拿着自己书写用户名密码去和shiro.ini 文件中的账号密码比较
UsernamePasswordToken token =new UsernamePasswordToken("root","111");
try {
//[6]进行身份的验证
subject.login(token);
//[7]通过方法判断是否登录成功
if(subject.isAuthenticated()){
System.out.println("登录成功");
}
} catch (IncorrectCredentialsException e) {
System.out.println("登录失败");
}catch (UnknownAccountException e){
System.out.println("用户名不正确");
}
}
} |
TestDemo.java
package com.bjsxt.shiro3;
import org.apache.shiro.crypto.hash.Md5Hash;
public class TestDemo {
public static void main(String[] args) {
//使用MD5加密
Md5Hash md5=new Md5Hash("1111");
System.out.println("1111=="+md5);
//加盐
md5=new Md5Hash("1111","sxt");
System.out.println("1111=="+md5);
//迭代次数
md5=new Md5Hash("123","sxt",2);
System.out.println("1111=="+md5);
}
} |
shiro-jdbc.ini
[main]
#设置securityManager中realm
credentialsMatcher=org.apache.shiro.authc. credential.HashedCredentialsMatcher
credentialsMatcher.hashAlgorithmName=md5
credentialsMatcher.hashIterations=2
userRealm=com.bjsxt.shiro3.UserRealm
userRealm.credentialsMatcher=$credentialsMatcher
securityManager.realms=$userRealm |
8.3 凭证匹配器
在Realm接口的实现类AuthenticatingRealm中有credentialsMatcher属性。
意为凭证匹配器。常用来设置加密算法及迭代次数等。
Shiro.ini
[main]
#配置凭证匹配器
credentialsMatcher=org.apache.shiro.authc. credential.HashedCredentialsMatcher
#设置凭证匹配器的相关属性
credentialsMatcher.hashAlgorithmName=MD5
credentialsMatcher.hashIterations=2
#配置Realm
customRealm=com.bjsxt.realms.CustomRealm
#配置Realm的凭证匹配器属性
customRealm.credentialsMatcher=$credentialsMatcher
#将Realm注入给SecurityManager
securityManager.realm=$customRealm |
costomRealm
package com.bjsxt.realms;
import java.net.ConnectException;
import java.security.interfaces.RSAKey;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.realm.AuthenticatingRealm;
import org.apache.shiro.util.ByteSource;
import com.mysql.jdbc.Driver;
public class CustomRealm extends AuthenticatingRealm
{
private String principal;
private String credentials;
private ResultSet rs;
private Statement state;
private Connection conn;
private String salt;
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken
token) throws AuthenticationException {
//使用JDBC,从数据库获取数据
try {
//1.注册驱动
Driver driver = new Driver();
DriverManager.registerDriver(driver);
//2.获取连接对象
String url ="jdbc:mysql:///test";
String user = "root";
String password = "victor";
conn = DriverManager.getConnection(url , user
, password );
state = conn.createStatement();
//4.执行sql语句
String sql = "select userName,passwd,passwd_salt
from starLogin";
rs = state.executeQuery(sql );
//5.处理结果集
while (rs.next()) {
principal = rs.getString("userName");
credentials = rs.getString("passwd");
salt = rs.getString("passwd_salt");
}
} catch (SQLException e) {
e.printStackTrace();
}finally{
if(rs != null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(state != null){
try {
state.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
ByteSource newSalt = ByteSource.Util.bytes(salt);
SimpleAuthenticationInfo simpleAuthenticationInfo
= new SimpleAuthenticationInfo(principal, credentials,newSalt
, "customRealm");
return simpleAuthenticationInfo;
}
}
|
AutenticationTest
package com.bjsxt.test;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
import org.junit.Test;
//实现简单认证
public class AuthenticationTest {
@Test
public void testAuthentication(){
//1.构建SecurityManager工厂
IniSecurityManagerFactory securityManagerFactory
= new IniSecurityManagerFactory("classpath:shiro.ini");
//2.通过securityManagerFactory工厂获取SecurityManager实例
SecurityManager securityManager = securityManagerFactory.getInstance();
//3.将securityManager设置到运行环境当中
SecurityUtils.setSecurityManager(securityManager);
//4.获取subject实例
Subject subject = SecurityUtils.getSubject();
//5.创建用户名密码验证令牌Token
UsernamePasswordToken token = new UsernamePasswordToken("victor","123456");
//6.进行身份验证
subject.login(token);
//7.判断是否认证通过
System.out.println(subject.isAuthenticated());
}
} |
授权
授权:给身份认证通过的任授予某些资源的访问权限
权限的粒度 粗粒度 细粒度
粗粒度
User 具有 CRUD 的操作 通常指的是表的操作
细粒度
只允许查询 id=1 的用户 使用业务代码实现
Shiro 的授权是粗粒度
角色:角色就是权限的集合
Shiro 中代码的实现
详细见代码
package com.bjsxt.shiro1;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
import java.util.Arrays;
public class TestA {
public static void main(String[] args) {
//[1]解析shiro.ini文件
Factory<SecurityManager> factory =new
IniSecurityManagerFactory("classpath:shiro.ini");
//[2]通过SecurityManager工厂获得SecurityManager实例
SecurityManager securityManager = factory.getInstance();
//[3]把SecurityManager对象设置到运行环境中
SecurityUtils.setSecurityManager(securityManager);
//[4]通过SecurityUtils获得主体subject
Subject subject = SecurityUtils.getSubject();
//[5]书写自己输入的账号和密码---相当于用户自己输入的账号和密码
//我们拿着自己书写用户名密码去和shiro.ini 文件中的账号密码比较
UsernamePasswordToken token =new UsernamePasswordToken("zs","123");
try {
//[6]进行身份的验证
subject.login(token);
} catch (IncorrectCredentialsException e)
{
System.out.println("登录失败");
}
//授权的查询
//基于角色的授权
boolean flag = subject.hasRole("role1");
//System.out.println(flag);
//判断是否具有多个角色
boolean[] booleans = subject.hasRoles(Arrays.asList
("role1", "role3"));
/*for(Boolean b:booleans){
System.out.println(b);
}*/
//可以使用checkRole判断指定用户是否具有对应角色
//如果指定用户下没有对应的角色就会抛出异常 UnauthorizedException
/* subject.checkRole("role3");
subject.checkRoles("role1","role2");*/
//基于资源的授权
boolean flag2 = subject.isPermitted("iii");
//System.out.println(flag2);
//判读是否具有多个资源
boolean permittedAll = subject.isPermittedAll
("add", "oo", "ii");
//通过checkPermission 进行判断指定用户下是否有指定的资源
//如果没有就会抛出UnauthorizedException
subject.checkPermission("uu");
subject.checkPermissions("ii","ooo","add");
}
} |
Role.java
package com.bjsxt.shiro1;
import org.apache.shiro.authz.annotation.RequiresRoles;
public class Role {
} |
Shiro 中的授权检查的 3 种方式
A、 编程式
B、 注解式 @RequiresRoles("管理员") public void
aa(){
}
C、 标签配置 <shiro:hasPermission name="add">
<a>添加操作</a> </shiro:hasPermission>
自定义 Realm 实现授权
我们仅仅通过配置文件指定授权是非常的不灵活的,在实际的应用中我们是将用户的信息和合权限信息保存到数据库中,我们是从数据库中获得用户的信息
,使用 JDBCRealm 进行授权 。使用 JDBCRealm 操作的时候也不是很灵活。所以我们一般使用自定义
Realm 实现授权。
package com.bjsxt.shiro;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;
public class UserRealm extends AuthorizingRealm
{
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo
(AuthenticationToken authenticationToken) throws
AuthenticationException {
//System.out.println(authenticationToken.getPrincipal());
try {
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection
("jdbc:mysql://127.0.0.1:3306/shiro",
"root", "root");
PreparedStatement prepareStatement = conn.prepareStatement
("select pwd from admin where uname =? ");
prepareStatement.setObject (1,authenticationToken.getPrincipal());
ResultSet rs = prepareStatement.executeQuery();
while (rs.next()){
SimpleAuthenticationInfo info=new SimpleAuthenticationInfo
(authenticationToken.getPrincipal(), rs.getString("pwd"),"userRealm");
return info;
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo
(PrincipalCollection principalCollection) {
String username = principalCollection.getPrimaryPrincipal().toString();
//获得username 然后去数据库查询这个用户对应的角色,在根据角色查询出指定角色下对应的菜单,
//返回给指定角色下的所有菜单--List集合
System.out.println("username="+username);
//模拟数据库查的菜单
List<String> list =new ArrayList<>();
list.add("updateUser");
list.add("addUser");
list.add("deleteUser");
SimpleAuthorizationInfo simpleAuthorizationInfo
=new SimpleAuthorizationInfo();
for(String l:list){
simpleAuthorizationInfo.addStringPermission(l);
}
return simpleAuthorizationInfo;
}
} |
|