对上述中的主体、资源、权限可以通过下面的数据模型来表示。
主体(账号、密码)
资源(资源名称、访问地址)
权限(权限名称、资源id)
角色(角色名称)
角色和权限的关系(角色id、权限id)
主体和角色的关系(主体id、角色id)
如下图:
if(user.hasRole("总经理角色id")){
//查询工资
}
if(user.hasRole("总经理角色id") || user.hasRole("部门经理角色id") ){
//查询工资
}
if(user.hasPermission("查询工资权限标识")){
//查询工资
}
对资源类型的管理称为粗颗粒度权限管理,即只控制到菜单、按钮、方法等。
比如:超级管理员可以访问用户添加、用户信息等页面,部门管理员可以访问用户信息页面。
对资源实例的管理称为细颗粒度权限管理,即控制到数据级别的权限管理。
比如:部门经理只可以访问本部门的员工信息,用户只能看到自己的菜单、大区经理只能看到本辖区的销售订单等等。
对于粗颗粒度的权限管理可以很容易的做到系统架构级别的功能,即系统功能操作使用统一的粗颗粒的权限管理。
对于细颗粒度的谦虚管理不建议做成系统架构级别的功能,因为对数据级别的控制是系统的业务需求,随着业务需求的变更业务功能变化的可能性很大,建议对数据级别的权限控制在业务层个性化开发。比如:部门经理只能查询本部门的员工信息,在service层提供一个部门id的参数的方法,controller层中根据当前用户的信息获取该用户属于那个部门,调用service的时候将部门的id传入进去,就可以实现部门经理只能查询本部门的员工的功能。
对于权限管理基本上每个系统都有,使用权限管理框架完成权限管理功能的开发可以节省系统开发时间,并且权限管理框架提供了完善的认证和授权功能有利于系统扩展维护,但是学习权限管理框架是需要成本的,所以选择一款简单高效的权限管理框架非常重要。
基于URL拦截是企业中常用的权限管理方式,实现思路是:将系统操作的每个URL配置到权限表中,将权限分配给角色,然后将角色分配给用户,用户访问系统功能的时候通过Filter或拦截器等进行过滤,过滤器或拦截器获取到用户访问的URL,只要访问到的URL是用户分配角色中的URL则放行继续访问。
如下图:
Shiro是Apache旗下的一个开源框架,它将软件系统的安全认证相关的功能抽取出来,实现用户身份认证、权限授权、加密、会话管理等功能,组成了一个通用的安全认证框架。
既然Shiro将安全认证相关的功能抽取出来组成一个框架,使用shiro就可以非常快速的完成认证、授权等功能的开发,降低系统成本。
shiro使用广泛,shiro可以运行在web应用,非web应用,集群分布式应用中越来越多的用户开始使用shiro。
Java领域中的Spring Security也是一个开源的权限管理框架,但是Spring Security依赖Spring运行,而Shiro就相对独立,最主要的是因为Shiro使用简单、灵活,所以现在越来越多的用户选择使用Shiro。
Realm即领域,相当于DataSource数据源,SecurityManager进行安全认证的时候需要通过Realm获取用户权限数据,比如:如果用户身份数据在数据库,那么Realm就需要从数据库获取用户身份信息。
注意:不要将Realm理解成只是从数据源取数据,在Realm中还有认证授权校验的相关代码。
Cryptography即密码管理,Shiro提供了一套加密/解密组件,方便开发。比如提供常用的散列、加密/解密等功能。
和其他Java开源框架类似,将shiro的jar包加入项目中就可以使用shiro提供的功能了。shiro-core是核心包必须选用,还提供了和web整合的shiro-web,和Spring整合的shiro-spring,和任务调度quartz整合的shiro-quartz等,下边是shiro各个jar包maven坐标。
log4j.rootLogger=debug, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m %n
[users]
zhangsan = 123456
lisi = 123456
package com.sunxiaping.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;
import org.junit.Test;
public class ShiroTest {
@Test
public void test() {
//创建SecurityManager工厂对象:加载配置文件,创建工厂对象
Factory<SecurityManager> securityManagerFactory = new IniSecurityManagerFactory("classpath:shiro.ini");
//获取SecurityManager对象
SecurityManager securityManager = securityManagerFactory.getInstance();
//将SecurityManager对象绑定到当前运行环境中,让系统随时随地都可以访问SecurityManager对象
SecurityUtils.setSecurityManager(securityManager);
//获取Subject主体对象
Subject subject = SecurityUtils.getSubject();
//执行登录
try {
subject.login(new UsernamePasswordToken("zhangsan", "123456"));
} catch (UnknownAccountException e) {
System.out.println("用户名不存在");
e.printStackTrace();
} catch (IncorrectCredentialsException e) {
System.out.println("用户名存在,密码不正确");
e.printStackTrace();
}
//判断是否登录成功
boolean authenticated = subject.isAuthenticated();
System.out.println("是否登录:" + authenticated);
//退出
subject.logout();
authenticated = subject.isAuthenticated();
System.out.println("是否登录:" + authenticated);
}
}
UnknownAccountException:账号不存在异常。
IncorrectCredentialsException:密码错误异常。
DisabledAccountException:账号被禁用异常。
LockedAccountException:账号被锁定异常。
ExcessiveAttemptsException:登录失败次数过多。
ExpiredCredentialsException:凭证过期。
……
上述的程序使用的是Shiro自带的IniRealm。IniRealm从ini配置文件中读取用户信息,大部分情况下需要从系统的数据库中读取用户信息,所以需要自定义Realm。
package com.sunxiaping.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.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
/**
* 自定义Realm
*/
public class CustomRealm extends AuthorizingRealm {
@Override
public String getName() {
return "customRealm";
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
return null;
}
/\*\*
\* 认证
\*
\* @param token
\* @return
\* @throws AuthenticationException
\*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
AuthenticationInfo info = null;
//获取身份信息 zhangsan
Object principal = token.getPrincipal();
//假设从数据库中查询到的zhangsan的密码是123456
String password = "123456";
//一般在实际开发中,SimpleAuthenticationInfo的第一个参数是查询到的用户对象,第二个参数是密码,第三个参数是getName()
info = new SimpleAuthenticationInfo(principal,password,getName());
return info;
}
}
[main]
customRealm = com.sunxiaping.shiro.CustomRealm
securityManager.realms=$customRealm
package com.sunxiaping.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;
import org.junit.Test;
public class ShiroTest {
@Test
public void test() {
//创建SecurityManager工厂对象:加载配置文件,创建工厂对象
Factory<SecurityManager> securityManagerFactory = new IniSecurityManagerFactory("classpath:shiro-realm.ini");
//获取SecurityManager对象
SecurityManager securityManager = securityManagerFactory.getInstance();
//将SecurityManager对象绑定到当前运行环境中,让系统随时随地都可以访问SecurityManager对象
SecurityUtils.setSecurityManager(securityManager);
//获取Subject主体对象
Subject subject = SecurityUtils.getSubject();
//执行登录
try {
subject.login(new UsernamePasswordToken("zhangsan", "123456"));
} catch (UnknownAccountException e) {
System.out.println("用户名不存在");
e.printStackTrace();
} catch (IncorrectCredentialsException e) {
System.out.println("用户名存在,密码不正确");
e.printStackTrace();
}
//判断是否登录成功
boolean authenticated = subject.isAuthenticated();
System.out.println("是否登录:" + authenticated);
//退出
subject.logout();
authenticated = subject.isAuthenticated();
System.out.println("是否登录:" + authenticated);
}
}
package com.sunxiaping.shiro;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.junit.Test;
public class SimpleHashTest {
@Test
public void test() {
String password = "123456";
Md5Hash md5Hash = new Md5Hash(password);
System.out.println(md5Hash.toString());
//加盐加密
String salt = "sunxiaping";
md5Hash = new Md5Hash(password, salt);
System.out.println(md5Hash.toString());
//加密:md5+盐+散列次数
md5Hash = new Md5Hash(password, salt, 3);
System.out.println(md5Hash.toString());
SimpleHash hash = new SimpleHash("SHA-1", password, salt, 3);
System.out.println(hash.toString());
}
}
package com.sunxiaping.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.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
/**
* 自定义Realm
*/
public class CustomRealm extends AuthorizingRealm {
@Override
public String getName() {
return "customRealm";
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
return null;
}
/\*\*
\* 认证
\*
\* @param token
\* @return
\* @throws AuthenticationException
\*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
AuthenticationInfo info = null;
//获取身份信息 zhangsan
Object principal = token.getPrincipal();
//假设从数据库中查询到的zhangsan的密码是123456
//md5+盐+散列次数(3)
String password = "9666025158cfc769d3d25e40cba317a3";
String salt = "sunxiaping";
//一般在实际开发中,SimpleAuthenticationInfo的第一个参数是查询到的用户对象,第二个参数是密码,第三个参数是getName()
info = new SimpleAuthenticationInfo(principal, password, ByteSource.Util.bytes(salt), getName());
return info;
}
}
[main]
credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher
credentialsMatcher.hashAlgorithmName=MD5
credentialsMatcher.hashIterations=3
customRealm = com.sunxiaping.shiro.CustomRealm
customRealm.credentialsMatcher=$credentialsMatcher
securityManager.realms=$customRealm
package com.sunxiaping.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;
import org.junit.Test;
public class ShiroTest {
@Test
public void test() {
//创建SecurityManager工厂对象:加载配置文件,创建工厂对象
Factory<SecurityManager> securityManagerFactory = new IniSecurityManagerFactory("classpath:shiro-cryptography.ini");
//获取SecurityManager对象
SecurityManager securityManager = securityManagerFactory.getInstance();
//将SecurityManager对象绑定到当前运行环境中,让系统随时随地都可以访问SecurityManager对象
SecurityUtils.setSecurityManager(securityManager);
//获取Subject主体对象
Subject subject = SecurityUtils.getSubject();
//执行登录
try {
subject.login(new UsernamePasswordToken("zhangsan", "123456"));
} catch (UnknownAccountException e) {
System.out.println("用户名不存在");
e.printStackTrace();
} catch (IncorrectCredentialsException e) {
System.out.println("用户名存在,密码不正确");
e.printStackTrace();
}
//判断是否登录成功
boolean authenticated = subject.isAuthenticated();
System.out.println("是否登录:" + authenticated);
//退出
subject.logout();
authenticated = subject.isAuthenticated();
System.out.println("是否登录:" + authenticated);
}
}
手机扫一扫
移动阅读更方便
你可能感兴趣的文章