Apache-Shiro基本使用指南
目录
Apache Shiro基本使用指南
前言
在Java Web开发中,安全框架的选择一直是开发者关注的重点。Spring Security功能强大但配置复杂,而Apache Shiro则以其简单易用、功能完整而受到广大开发者的喜爱。今天我们就来详细了解Shiro的基本使用,看看如何快速上手这个优秀的安全框架。
Shiro简介
Apache Shiro是一个功能强大且易于使用的Java安全框架,它可以帮助我们完成认证、授权、加密、缓存和会话管理等安全相关的功能。
Shiro的核心概念
- Subject(主体):当前用户,不一定是具体的人,也可能是第三方服务
- SecurityManager(安全管理器):Shiro的核心,协调各个组件工作
- Realm(域):数据源,负责获取安全数据(用户、角色、权限)
环境搭建
首先,我们需要在项目中引入Shiro的相关依赖。以Maven项目为例:
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- Shiro核心依赖 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
快速入门示例
让我们从一个最简单的示例开始,了解Shiro的基本工作流程。
1. 创建shiro.ini配置文件
在resources目录下创建shiro.ini
文件:
[users]
# 用户名=密码,角色1,角色2
zhang=123,admin
wang=456,user
li=789,user,manager
[roles]
# 角色=权限1,权限2
admin=*
user=user:read,user:create
manager=user:*,order:read
2. 编写测试代码
public class ShiroQuickStart {
public static void main(String[] args) {
// 1. 获取SecurityManager工厂,此处使用Ini配置文件初始化SecurityManager
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
// 2. 得到SecurityManager实例并绑定给SecurityUtils
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
// 3. 得到Subject及创建用户名/密码身份验证Token
Subject currentUser = SecurityUtils.getSubject();
// 4. 登录
if (!currentUser.isAuthenticated()) {
UsernamePasswordToken token = new UsernamePasswordToken("zhang", "123");
token.setRememberMe(true);
try {
currentUser.login(token);
logger.info("用户登录成功");
} catch (UnknownAccountException uae) {
logger.error("用户不存在: " + token.getPrincipal());
} catch (IncorrectCredentialsException ice) {
logger.error("密码错误: " + token.getPrincipal());
} catch (LockedAccountException lae) {
logger.error("账户被锁定: " + token.getPrincipal());
} catch (AuthenticationException ae) {
logger.error("认证失败");
}
}
// 5. 权限检查
if (currentUser.hasRole("admin")) {
logger.info("用户拥有admin角色");
} else {
logger.info("用户没有admin角色");
}
// 检查具体权限
if (currentUser.isPermitted("user:create")) {
logger.info("用户有创建用户的权限");
} else {
logger.info("用户没有创建用户的权限");
}
// 6. 退出登录
currentUser.logout();
}
}
自定义Realm
在实际项目中,我们通常需要从数据库获取用户信息,这时就需要自定义Realm。
创建shiro.ini配置文件
[main]
#声明realm
myClass = com.coldscholor.shiro.MyRealm
#注册realm到securityManager中
securityManager.realm = $myClass
创建自定义Realm类
/**
* Realm域:Shiro从Realm获取安全数据(如用户、角色、权限),
* 就是说SecurityManager要验证用户身份,那么它需要从Realm
* 获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm
* 得到用户相应的角色/权限进行验证用户是否能进行操作;可以把
* Realm看成DataSource,即安全数据源
* <p>
* 自定义realms对象
* 继承AuthorizingRealm
* 重写方法
* doGetAuthorizationInfo:授权
* 获取到用户的授权数据(用户的权限数据)
* doGetAuthenticationInfo:认证
* 根据用户名密码登录,将用户数据保存(安全数据)
*/
public class MyRealm extends AuthorizingRealm {
public MyRealm() {
// 设置密码匹配器
HashedCredentialsMatcher matcher = new HashedCredentialsMatcher(DigestsUtil.SHA1);
// 设置迭代次数
matcher.setHashIterations(DigestsUtil.COUNTS);
// 设置加密方式
setCredentialsMatcher(matcher);
}
/**
* 授权
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("授权");
// 获取用户名
String username = (String) principalCollection.getPrimaryPrincipal();
System.out.println("用户名:" + username);
// 用户的权限数据
List<String> perms = new ArrayList<>();
perms.add("save");
perms.add("update");
perms.add("delete");
perms.add("find");
// 用户的角色数据
List<String> roles = new ArrayList<>();
roles.add("admin");
roles.add("user");
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addStringPermissions(perms);
info.addRoles(roles);
return info;
}
/**
* 认证
*
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
/*@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("认证");
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
String username = token.getUsername();
String password = new String(token.getPassword());
if ("admin".equals(username) && "123456".equals(password)) {
// 参数:用户名,密码,当前realm对象的名称
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(username, password, getName());
return info;
} else {
throw new RuntimeException("用户名或密码错误,认证失败");
}
}*/
/**
* 对密文密码进行解密并认证
* salt:是一个随机字符串,用于对密码进行加密
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("认证");
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
String username = token.getUsername();
SecurityService securityService = new SecurityServiceImpl();
Map<String,String> map = securityService.findPasswordByLoginName(username);
if(map == null){
throw new RuntimeException("用户不存在");
}
String salt = map.get("salt");
String password = map.get("password");
//参数1:安全数据 参数2:密码 参数3:混淆字符串 参数4:realm名称
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(username, password, ByteSource.Util.bytes(salt), getName());
return info;
}
}
测试结果
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
认证
登录结果:true
总结
Apache Shiro作为一个轻量级的安全框架,具有简单易用、功能完整的特点。在实际项目中,建议根据具体的业务需求选择合适的配置方式,并结合缓存、异常处理等机制,构建一个安全、高效的权限管理系统。Shiro虽然简单,但功能强大。掌握了这些基本用法,相信你已经能够在项目中熟练使用Shiro来处理安全相关的需求了。