权限管理
基本上涉及到用户参与的系统都要进行权限管理,权限管理属于系统安全的范畴,权限管理实现对用户访问系统的控制,按照安全规则或者安全策略控制用户可以访问而且只能访问自己被授权的资源。
权限管理包括用户身份认证和授权两部分,简称认证授权。对于需要访问控制的资源用户首先经过身份认证,认证通过后用户具有该资源的访问权限方可访问。
身份验证
身份认证,就是判断一个用户是否为合法用户的处理过程。最常用的简单身份认证方式是系统通过核对用户输入的用户名和口令,看其是否与系统中存储的该用户的用户名和口令一致,来判断用户身份是否正确。对于采用指纹等系统,则出示指纹;对于硬件Key等刷卡系统,则需要刷卡。
授权
授权,即访问控制,控制谁能访问哪些资源。主体进行身份认证后需要分配权限方可访问系统的资源,对于某些资源没有权限是无法访问的。
认证授权框架
shiro框架和spring security框架 这款框架是现在市面比较流行。
Shiro:用于中小型项目比较常见,简单易上手,可以支持多种环境
Shiro 可以不跟任何的框架或者容器绑定,可独立运行
Spring Security:一般多用于spring环境,中大型项目,更强大
Spring Security 则必须要有Spring环境
1、shiro框架简介
Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。
2、shiro框架的优点
1、 简单的身份验证,支持多种数据源
2、对角色的简单授权,支持细粒度的授权(方法)
3、支持一级缓存,以提升应用程序的性能
4、内置基于POJO的企业会话管理,适用于web及非web环境
5、非常简单的API加密
6、不跟任何框架绑定,可以独立运行
3、架构
4、使用shiro完成认证工作
4.1 shiro中认证的关键对象
Subject(用户):当前的操作用户 获取当前用户Subject currentUser = SecurityUtils.getSubject()
SecurityManager(安全管理器):Shiro的核心,负责与其他组件进行交互,实现 subject 委托的各种功能
Realms(数据源):Realm会查找相关数据源,充当与安全管理间的桥梁,经过Realm找到数据源进行认证,授权等操作
Authenticator(认证器): 用于认证,从 Realm 数据源取得数据之后进行执行认证流程处理。
Authorizer(授权器):用户访问控制授权,决定用户是否拥有执行指定操作的权限。
SessionManager (会话管理器):支持会话管理
CacheManager (缓存管理器):用于缓存认证授权信息
Cryptography(加密组件):提供了加密解密的工具包
4.2 认证流程
4.3 实现
没有连接数据库,使用ini文件测试
(1) 创建一个maven java工程
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.9.0</version>
</dependency>
(2)创建ini文件
INI文件由节、键、值组成。
节:[section]
参数(键=值):name=value
注解:注解使用分号表示(;)或者(#)表示 。在分号后面的文字,直到该行结尾都全部为注解。
(3)测试代码
public class TestShiro {
public static void main(String[] args) {
//1.获取SecurityManager对象
DefaultSecurityManager securityManager = new DefaultSecurityManager();
//2.读取ini文件
IniRealm iniRealm = new IniRealm("classpath:shiro.ini");
//3、设置securityManager的realm
securityManager.setRealm(iniRealm);
//4.设置securityManager上下文生效
SecurityUtils.setSecurityManager(securityManager);
//获取subject主体对象
Subject subject = SecurityUtils.getSubject();
try {
//UsernamePasswordToken的作用是封装输入的用户名和密码
UsernamePasswordToken token = new UsernamePasswordToken("admin","123456");
//抛出异常 比对shiro中realm和自己输入的token做对比
subject.login(token);
System.out.println("success");
} catch (Exception e) {
e.printStackTrace();
System.out.println("账号或密码错误");
}
}
}
4.4认证的原理
Subject: 主题 登录信息提交给SecurityManager, --->认证器Authenticator---->根据你的realm提供的数据进行相关的认证。 realm---与数据源交互的类。
5、授权
5.1 授权流程
5.2 修改ini文件
;定义用户 账号=密码,具有的角色
[users]
admin=123456,role1,role2
zhangsan=123456,role1
# 定义角色 角色名称=具有的权限
[roles]
role1=user:query,user:export
role2=user:delete,user:update,user:insert
5.3 修改代码
public class TestShiro {
public static void main(String[] args) {
//1.获取SecurityManager对象
DefaultSecurityManager securityManager = new DefaultSecurityManager();
//2.读取ini文件
IniRealm iniRealm = new IniRealm("classpath:shiro.ini");
//3、设置securityManager的realm
securityManager.setRealm(iniRealm);
//4.设置securityManager上下文生效
SecurityUtils.setSecurityManager(securityManager);
//获取subject主体对象
Subject subject = SecurityUtils.getSubject();
try {
//UsernamePasswordToken的作用是封装输入的用户名和密码
UsernamePasswordToken token = new UsernamePasswordToken("admin","123456");
//抛出异常 比对shiro中realm和自己输入的token做对比
subject.login(token);
System.out.println("success");
} catch (Exception e) {
e.printStackTrace();
System.out.println("账号或密码错误");
}
//退出登录
//subject.logout();
System.out.println("~~~~~~~~~~~登录成功后~~~~~~~~~~~~~");
//是否真正成功,认证成功返回true,认证失败返回false
boolean authenticated = subject.isAuthenticated();
System.out.println("是否认证成功:"+authenticated);
if (authenticated){
//判断该用户使用具有指定权限
boolean permitted = subject.isPermitted("user:query");
System.out.println("该用户是否具有查询权限:"+permitted);
//判断当前用户是否具有指定角色
boolean role1 = subject.hasRole("role1");
System.out.println("该用户是否具有角色1的权限:"+role1);
}else {
System.out.println("请先登录~~~~~~~~~~~");
}
}
}
6、认证源码流程
我们从源码中可以观察到,我们的最终认证交于realm完成认证功能,返回一个info, 如果info不为null,则进行密码比对。如果我们希望用数据库中的账号和密码完成认证功能,则只需要我们自定义一个类并继承AuthenticatingRealm。
7、自定义Realm认证
public class MyRealm extends AuthenticatingRealm {
private UserService userService = new UserService();
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//根据token获取账号
String principal = (String) authenticationToken.getPrincipal();
//根据账号查询信息
User user = userService.findByUsername(principal);
//System.out.println(user);
if (user!=null){
//从数据库中获取密码
//SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user,user.getPassword(),this.getName());
//设置加密时的盐
ByteSource source = ByteSource.Util.bytes(user.getSalt());
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user,user.getPassword(),source,this.getName());
return info;
}
return null;
}
}
8、加密
shiro它提供了很多种加密器。其他使用最多就是HashedCredentialsMatcher。它的底层使用的还是Md5加密。了解一下MD5加密
public class TestPwd {
public static void main(String[] args) {
//source是需要加密的明文
Md5Hash md5Hash = new Md5Hash("123456");
System.out.println(md5Hash);
//salt盐
Md5Hash md5Hash1 = new Md5Hash("123456", "asd");
System.out.println(md5Hash1);
//设置n次加密
Md5Hash md5Hash2 = new Md5Hash("123456", "dumpling", 1024);
System.out.println(md5Hash2);
}
}
8.1 shiro使用密码加密器
9、自定义realm的授权功能
public class MyRealm2 extends AuthorizingRealm {
private UserService userService = new UserService();
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
User primaryPrincipal = (User) principalCollection.getPrimaryPrincipal();
List permission = userService.findPermissionByUsername(primaryPrincipal.getUsername());
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
simpleAuthorizationInfo.addStringPermissions(permission);
return simpleAuthorizationInfo;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//根据token获取账号
String principal = (String) authenticationToken.getPrincipal();
//根据账号查询信息
User user = userService.findByUsername(principal);
//System.out.println(user);
if (user!=null){
//从数据库中获取密码
//SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user,user.getPassword(),this.getName());
//设置加密时的盐
ByteSource source = ByteSource.Util.bytes(user.getSalt());
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user,user.getPassword(),source,this.getName());
return info;
}
return null;
}
}
测试:
public class TestShiro03 {
public static void main(String[] args) {
//1.获取SecurityManager对象
DefaultSecurityManager securityManager = new DefaultSecurityManager();
//创建realm对象
MyRealm2 myRealm = new MyRealm2();
//为realm指定加密器
HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
//设置加密算法
credentialsMatcher.setHashAlgorithmName("MD5");
//设置加密方式
credentialsMatcher.setHashIterations(1024);
//为realm设置加密器
myRealm.setCredentialsMatcher(credentialsMatcher);
securityManager.setRealm(myRealm);
//4.设置securityManager上下文生效
SecurityUtils.setSecurityManager(securityManager);
//获取subject主体对象
Subject subject = SecurityUtils.getSubject();
try {
//UsernamePasswordToken的作用是封装输入的用户名和密码
UsernamePasswordToken token = new UsernamePasswordToken("admin","123456");
//抛出异常 比对shiro中realm和自己输入的token做对比
subject.login(token);
System.out.println("success");
} catch (Exception e) {
e.printStackTrace();
System.out.println("账号或密码错误");
}
//退出登录
//subject.logout();
System.out.println("~~~~~~~~~~~登录成功后~~~~~~~~~~~~~");
//是否真正成功,认证成功返回true,认证失败返回false
System.out.println(subject.isPermitted("user:query"));
PrincipalCollection principals = subject.getPrincipals();
System.out.println(subject.isPermitted("user:insert"));
}
}