Apache Shiro 是一个强大而灵活的开源安全框架,它干净利落地处理身份认证,授权,企业会话管理和加密
Shiro官网Authentication:身份的验证-就是我们平时做的登录
Authorization:授权:赋予角色不同的 菜单功能
SessionManagement:管理登录用户的信息
Cryptography:加密技术 MD5加密算法等
WebSupport:shiro 对 web 项目进行的支持
Caching:缓存 可以安全快速的操作
Concurrency:支持并发多线程的处理
Testing:测试
RunAs:可以实现在一个用户允许的前提下,使用另一个用户访问
RememberMe:记住我
<dependency>
<groupId>org.apache.shirogroupId>
<artifactId>shiro-coreartifactId>
<version>1.5.3version>
dependency>
<dependency>
<groupId>org.apache.shirogroupId>
<artifactId>shiro-springartifactId>
<version>1.5.3version>
dependency>
<dependency>
<groupId>org.apache.shirogroupId>
<artifactId>shiro-webartifactId>
<version>1.5.3version>
dependency>
[users]
uid=123
pwd=root
package com.alvin.shiro;
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 Test {
public static void main(String[] args) {
//解析shiro.ini文件
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
//通过SecurityManager工厂获得SecurityManager实例
SecurityManager securityManager = factory.getInstance();
//把SecurityManager对象设置到运行环境中
SecurityUtils.setSecurityManager(securityManager);
//通过SecurityManager 获得主体对象subject
Subject subject = SecurityUtils.getSubject();
//书写自己输入的账号和密码---相当于用户自己输入的账号和密码
UsernamePasswordToken token = new UsernamePasswordToken("uid","123");
try {
//进行身份验证
subject.login(token);
//通过方法判断是否登录成功
if(subject.isAuthenticated()){
System.out.println("登录成功");
}
} catch (IncorrectCredentialsException e) {
System.out.println("登录失败");
}catch (UnknownAccountException e){
System.out.println("用户名不正确");
}
}
}
账户失效异常
竞争次数过多
尝试次数过多
用户名不正确
凭证(密码)不正确
凭证过期
import org.apache.shiro.realm.jdbc.JdbcRealm;
添加依赖
<dependency>
<groupId>com.mchangegroupId>
<artifactId>c3p0artifactId>
<version>0.9.5.4version>
dependency>
从刚刚的源码我们知道,我们创建的数据库名必须和源码里面的一样,而且也必须包含源码里面涉及的字段才可以
[main]
#获得数据源
dataSou=com.mchange.v2.c3p0.ComboPooledDataSource
dataSou.driverClass=com.mysql.cj.jdbc.Driver
dataSou.jdbcUrl=jdbc:mysql://127.0.0.1:3306/shiro?useSSL=false&useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
dataSou.user=root
dataSou.password=root
#配置了jdbcRealm
jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealm
jdbcRealm.dataSource=$dataSou
#设置了securityManager
securityManager.realm=$jdbcRealm
package com.alvin.shiro;
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 TestS {
public static void main(String[] args) {
//解析shiro.ini文件
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro-jdbc.ini");
//通过SecurityManager工厂获得SecurityManager实例
SecurityManager securityManager = factory.getInstance();
//把SecurityManager对象设置到运行环境中
SecurityUtils.setSecurityManager(securityManager);
//通过SecurityManager 获得主体对象subject
Subject subject = SecurityUtils.getSubject();
//书写自己输入的账号和密码---相当于用户自己输入的账号和密码
UsernamePasswordToken token = new UsernamePasswordToken("黄贵根","123");
try {
//进行身份验证
subject.login(token);
//通过方法判断是否登录成功
if(subject.isAuthenticated()){
System.out.println("登录成功");
}
} catch (IncorrectCredentialsException e) {
System.out.println("登录失败");
}catch (UnknownAccountException e){
System.out.println("用户名不正确");
}
}
}
如果一个(或更多)Realm 验证成功,则整体的尝试被认为是成功的。如果没有一个验证成功,则整体尝试失败 类似于 java 中的 &
只有第一个成功地验证的 Realm 返回的信息将被使用。所有进一步的 Realm 将被忽略。如果没有一个验证成功,则整体尝试失败。类似于 java 中的 &&
为了整体的尝试成功,所有配置的 Realm 必须验证成功。如果没有一个验 证成功,则整体尝试失败
[main]
#获得数据源
dataSou=com.mchange.v2.c3p0.ComboPooledDataSource
dataSou.driverClass=com.mysql.cj.jdbc.Driver
dataSou.jdbcUrl=jdbc:mysql://127.0.0.1:3306/shiro?useSSL=false&useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
dataSou.user=root
dataSou.password=root
#配置了jdbcRealm
jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealm
jdbcRealm.dataSource=$dataSou
#配置验证器
authenticationStrategy=org.apache.shiro.authc.pam.FirstSuccessfulStrategy
#设置了securityManager
securityManager.realm=$jdbcRealm
securityManager.authenticator.authenticationStrategy=$authenticationStrategy
我们使用 JDBCRealm 的时候发现,shiro 的底层自己封装了数据库表的名称和字段的名称,这样就造成了使用起来非常不方便
package com.alvin.shiroplus;
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.*;
public class UserRealm extends AuthorizingRealm {
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
Class.forName("com.mysql.cj.jdbc.Driver");
connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/shiro?useSSL=false&useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai", "root", "root");
preparedStatement = connection.prepareStatement("select * from admin");
resultSet = preparedStatement.executeQuery();
while(resultSet.next()){ //这个名字随便定义
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(resultSet.getString("uname"),resultSet.getString("pwd"),"userRealm");
return info;
}
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}finally {
try {
connection.close();
preparedStatement.close();
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
return null;
}
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
}
[main]
#设置securityManager中realm
userRealm=com.alvin.shiroplus.UserRealm
securityManager.realms=$userRealm
package com.alvin.test;
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 Test {
public static void main(String[] args) {
//解析shiro.ini文件
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro-jdbcPlusPro.ini");
//通过SecurityManager工厂获得SecurityManager实例
SecurityManager securityManager = factory.getInstance();
//把SecurityManager对象设置到运行环境中
SecurityUtils.setSecurityManager(securityManager);
//通过SecurityManager 获得主体对象subject
Subject subject = SecurityUtils.getSubject();
//书写自己输入的账号和密码---相当于用户自己输入的账号和密码
UsernamePasswordToken token = new UsernamePasswordToken("黄贵根","123");
try {
//进行身份验证
subject.login(token);
//通过方法判断是否登录成功
if(subject.isAuthenticated()){
System.out.println("登录成功");
}
} catch (IncorrectCredentialsException e) {
System.out.println("登录失败");
}catch (UnknownAccountException e){
System.out.println("用户名不正确");
}
}
}
在身份认证的过程中往往都会涉及到加密,如果不加密,这个时候信息就会非常的不安全,shiro 中提供的算法比较多 如 MD5SHA 等
package com.alvin.test;
import org.apache.shiro.crypto.hash.Md5Hash;
public class Test {
public static void main(String[] args) {
//使用MD5加密
Md5Hash md5Hash = new Md5Hash("3696");
System.out.println("3696 = " + md5Hash);
//加盐
md5Hash = new Md5Hash("3696","alvin");
System.out.println("3696 = " + md5Hash);
//迭代次数
md5Hash = new Md5Hash("3696","alvin",2);
System.out.println("3696 = " + md5Hash);
}
}
[main]
#设置securityManager中realm
credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher
credentialsMatcher.hashAlgorithmName=md5
credentialsMatcher.hashIterations=2
userRealm=com.alvin.test.UserRealm
userRealm.credentialsMatcher=$credentialsMatcher
securityManager.realms=$userRealm
package com.alvin.test;
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 org.apache.shiro.util.ByteSource;
import java.sql.*;
public class UserRealm extends AuthorizingRealm {
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
Class.forName("com.mysql.cj.jdbc.Driver");
connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/shiro?useSSL=false&useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai", "root", "root");
preparedStatement = connection.prepareStatement("select * from admin");
resultSet = preparedStatement.executeQuery();
while(resultSet.next()){ //这里写盐值
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(resultSet.getString("uname"),resultSet.getString("pwd"), ByteSource.Util.bytes("alvin"),"userRealm");
return info;
}
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}finally {
try {
connection.close();
preparedStatement.close();
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
return null;
}
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
}
package com.alvin.test;
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 Test {
public static void main(String[] args) {
//解析shiro.ini文件
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro-jdbcPlusProMax.ini");
//通过SecurityManager工厂获得SecurityManager实例
SecurityManager securityManager = factory.getInstance();
//把SecurityManager对象设置到运行环境中
SecurityUtils.setSecurityManager(securityManager);
//通过SecurityManager 获得主体对象subject
Subject subject = SecurityUtils.getSubject();
//书写自己输入的账号和密码---相当于用户自己输入的账号和密码
UsernamePasswordToken token = new UsernamePasswordToken("黄贵根","3696");
try {
//进行身份验证
subject.login(token);
//通过方法判断是否登录成功
if(subject.isAuthenticated()){
System.out.println("登录成功");
}
} catch (IncorrectCredentialsException e) {
System.out.println("登录失败");
}catch (UnknownAccountException e){
System.out.println("用户名不正确");
}
}
}
授权:给身份认证通过的人授予某些资源的访问权限
权限的粒度我们一般分为两种,一种是粗粒度,一种是细粒度
粗粒度通常指的是表的操作,比如User 具有 CRUD 的操作
而细粒度的话,则是使用业务层代码的实现。比如只允许查询 id=1 的用户
授权说白了也是CRUD操作,和RBAC差不多,所以也是粗粒度
角色则是权限的集合
#指定具体的用户
[users]
uid=123,role1,role2
pwd=root
#角色的定义
[roles]
role1=add,update,delete
role2=find
package com.alvin.shiro;
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 Test {
public static void main(String[] args) {
//解析shiro.ini文件
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
//通过SecurityManager工厂获得SecurityManager实例
SecurityManager securityManager = factory.getInstance();
//把SecurityManager对象设置到运行环境中
SecurityUtils.setSecurityManager(securityManager);
//通过SecurityManager 获得主体对象subject
Subject subject = SecurityUtils.getSubject();
//书写自己输入的账号和密码---相当于用户自己输入的账号和密码
UsernamePasswordToken token = new UsernamePasswordToken("uid","123");
try {
//进行身份验证
subject.login(token);
} catch (IncorrectCredentialsException e) {
System.out.println("登录失败");
}
//授权的查询
//基于角色的授权
boolean role1 = subject.hasRole("role1");
System.out.println(role1);
//判断是否具有多个角色
boolean[] booleans = subject.hasRoles(Arrays.asList("role1", "role3"));
for(boolean flag : booleans){
System.out.println(flag);
}
//可以使用checkRole判断指定用户是否具有对应的角色
//如果没有指定角色,就会抛出异常
subject.checkRole("role3"); //3是不存在的角色
}
}
//基于资源的授权
boolean permitted = subject.isPermitted("add");
System.out.println(permitted);
//判断是否具有多个资源
boolean permittedAll = subject.isPermittedAll("add", "bob", "alice");
System.out.println(permittedAll);
//判断用户是否有指定资源,如果没有就抛出异常
subject.checkPermissions("uid","hello");
[main]
#设置securityManager中realm
userRealm=com.alvin.test.UserRealm
securityManager.realms=$userRealm
package com.alvin.test;
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 {
try {
Class.forName("com.mysql.cj.jdbc.Driver");
Connection conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/shiro?useSSL=false&useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai", "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;
}
}
package com.alvin.test;
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 Test {
public static void main(String[] args) {
/*Realm*/
//解析shiro.ini文件
Factory<SecurityManager> factory =new IniSecurityManagerFactory("classpath:shiro.ini");
//通过SecurityManager工厂获得SecurityManager实例
SecurityManager securityManager = factory.getInstance();
//把SecurityManager对象设置到运行环境中
SecurityUtils.setSecurityManager(securityManager);
//通过SecurityUtils获得主体subject
Subject subject = SecurityUtils.getSubject();
//书写自己输入的账号和密码---相当于用户自己输入的账号和密码
//我们拿着自己书写用户名密码去和shiro.ini 文件中的账号密码比较
UsernamePasswordToken token =new UsernamePasswordToken("黄贵根","3696");
try {
//进行身份的验证
subject.login(token);
//通过方法判断是否登录成功
if(subject.isAuthenticated()){
System.out.println("登录成功");
//授权的校验
System.out.println("是否存在该菜单:"+subject.isPermitted("updateUser2123"));
}
} catch (IncorrectCredentialsException e) {
System.out.println("登录失败");
}catch (UnknownAccountException e){
System.out.println("用户名不正确");
}
}
}