shiro是什么
shiro是apache的一个开源安全框架,相比spring的security显的更轻量。
核心组件:
- SecurityManager,大哥,它负责安全认证与授权
- Subject,主体,可以是用户也可以是第三方服务
- Realm,当需要与安全数据交互时,shiro就会从realm中去查找相应数据
创建一个简单的shiro项目
添加依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
构建一个简单的用户密码认证
/**
* com.qd
*
* @author liyawei
* @date 2018/4/25
*/
public class ShiroTest {
@Test
public void testShiro(){
//初始化一个SimpleAccountRealm并设置一个账号密码
SimpleAccountRealm simpleAccountRealm = new SimpleAccountRealm();
simpleAccountRealm.addAccount("xiaodou","123456");
//构建SecurityManager环境
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
defaultSecurityManager.setRealm(simpleAccountRealm);
//构建主体Subject
SecurityUtils.setSecurityManager(defaultSecurityManager);
Subject subject = SecurityUtils.getSubject();
//构建一个用户密码令牌模拟登陆
UsernamePasswordToken token = new UsernamePasswordToken("xiaodou","123456");
subject.login(token);
System.out.println("isAuthenticated:"+subject.isAuthenticated());
}
}
以上整个过程是:Subject带着用户名和密码去找SecurityManager申请认证,SecurityManager会去Realm中查找安全数据并返回认证信息
这里我们账号密码是正确的,所以认证成功了。如果账号或密码错误会认证失败
使用ini配置进行验证
在resources里建一个ini配置文件,设置用户密码信息和角色权限信息
# Users and their (optional) assigned roles
# username = password, role1, role2, ..., roleN
[users]
xiaodou=123456,admin
xiaobai=666666,guest
# Roles with assigned permissions
# roleName = perm1, perm2, ..., permN
[roles]
admin=*
guest=user:look
从ini文件中获取SecurityManage并认证角色和权限,和上面那个差不多
/**
* com.qd
*
* @author liyawei
* @date 2018/4/25
*/
public class ShiroIniTest {
@Test
public void test(){
// 从ini文件创建一个iniSecurityManager工厂,并获取一个SecurityManage实例
IniSecurityManagerFactory iniSecurityManagerFactory = new IniSecurityManagerFactory("classpath:user.ini");
SecurityManager securityManager = iniSecurityManagerFactory.getInstance();
// 设置SecurityManager到静态内存区,单例模式,并获取一个主体Subject
SecurityUtils.setSecurityManager(securityManager);
Subject subject = SecurityUtils.getSubject();
// 构建一个用户密码进行登录
UsernamePasswordToken token = new UsernamePasswordToken("xiaobai", "666666");
subject.login(token);
// 查看用户是否认证成功
System.out.println("isAuthenticated:"+subject.isAuthenticated());
// 查看用户是否有指定的角色
System.out.println("hasRole:"+subject.hasRole("admin"));
// 查看用户是否有指定的权限
System.out.println("isPermission:"+subject.isPermitted("user:delete"));
}
}
这里用户xiaobai并没有admin
的角色和user:delete
的权限所以下面两个是false
当然角色权限信息我们可以不用打印出来,实际开发中可能会try-catch捕捉到异常信息,根据不同的错误做不同的处理
自定义Realm
新建一个CustomRealm类继承AuthorizingRealm并实现doGetAuthorizationInfo和doGetAuthenticationInfo两个抽象方法
/**
* com.qd
*
* @author liyawei
* @date 2018/4/25
*/
public class CustomRealm extends AuthorizingRealm {
private UserService userService = new UserServiceImpl();
// 授权
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
User user = (User) principalCollection.getPrimaryPrincipal();
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
// 根据用户名从数据库中查询角色信息,返回值自己根据需求来写
List<Permission> permissionList = permissionService.getByUsername(username);
// 菜单+按钮权限
for (Permission permission : permissionList) {
if (StringUtil.isNotEmpty(permission.getCode())){
info.addStringPermission(permission.getCode());
}
}
return info;
}
// 认证
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
// 从token中获取用户名
String username = (String) token.getPrincipal();
// 从数据库中获取用户
User user = userService.getByUsername(username);
if (user == null) {
return null;
}
// 根据用户名从数据库中查询角色信息,返回值自己根据需求来写
Map<String, List<Permission>> permissionMap = permissionService.getByPermissionByUserId(user.getId());
// 获取菜单权限和按钮权限
user.setMenuList(permissionMap.get("menuList"));
user.setPermissionList(permissionMap.get("permissionList"));
// 返回认证信息
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(username, user.getPassword(), getName());
return simpleAuthenticationInfo;
}
}
认证时跟前面两种方式一样,只是defaultSecurityManager.setRealm(CustomRealm);
这里改一下就行,这里就不多写垃圾代码了