根据前面的了解,这次把基于权限的授权通过从数据库中初始化授权实现。
1、分析一下 Shiro 过滤链
/login = anon
/logout = logout
/admin/userlist = perms[userlist]
/admin/addUser = perms[addUser]
/admin/** = authc
/**= anon
点击 filterChainDefinitions 属性, 进去查看源码
1)可以看到它是 ShiroFilterFactoryBean 的一个属性:
2)经过一系列初始化后,它调用了 setFilterChainDefinitionMap 这个方法,点开这个方法:
3)通过 debug的方式 可以看到在初始化时, filterChainDefinitionMap是一个LinkedHashMap,其内容就是我们在配置文件中配置的内容
所以我们可以配置一个单独的 bean filterChainDefinitionMap 作为ShiroFilterFactoryBean的属性。 然后通过实例工厂的方式来产生Map通过实例工厂注册的Map实例,然后我们需要去新建并配置一个Bean
2、分析后,修改spring.xml数据库:
将 授权信息 放到 数据库中(事前约束规定,perms使用p: roles 使用 r: )
其他几个表和之前一样,没变化。
用户admin, 只拥有角色 admin 和 user 与访问 /admin/userlist 的权限资源,如果 pid 为3和4,则两者都可访问。
注意:
1)t_permission 的 数据顺序:从上到下,拦截范围必须是从小到大的
2)用户表 model 实现 序列化:
1) 配置bean
2)创建这个bean
import cn.jq.ssm.dao.PermissionMapper;
import cn.jq.ssm.model.Permission;
public class FilterChainDefinitionMapFactory {
@Autowired
private PermissionMapper permissionMapper;
public Map getFilterChainDefinitionMap(){
//从数据库中获取数据
List permissions = permissionMapper.getAllPermissions();
LinkedHashMap permsMap = new LinkedHashMap();
for (Permission permission : permissions) {
if(permission.getPname().contains("p:")) {
//构造perms[userlist]
String perms = permission.getPname().replace("p:", ""); //删除前缀
permsMap.put(permission.getUrl(), "perms["+ perms +"]");
}else {
permsMap.put(permission.getUrl(), permission.getPname());
}
}
return permsMap;
}
}
PermissionMapper 方法:
select
p.id,p.pname,p.url
from
t_permission p
3)自定义 ShiroRealm 类, 对于约束 p: 也做相应处理
public class ShiroRealm extends AuthorizingRealm{
/*
* public class ShiroRealm extends AuthenticatingRealm{
*/
@Autowired
private UserMapper userMapper;
@Autowired
private RoleMapper RoleMapper;
@Autowired
private PermissionMapper permissionMapper;
/**
* 在 shiro 中专门做登录验证的方法
* @param token
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
UsernamePasswordToken token2 = (UsernamePasswordToken) token;
String username = token2.getUsername();
User user = userMapper.getUserByUsername(username);
if(user == null) {
throw new UnknownAccountException("用户名或密码有误!");
}
if(user.getStatus() == 0) {
throw new UnknownAccountException("用户名已被禁用,请联系系统管理员!");
}
/**
* principals: 可以使用户名,或d登录用户的对象
* hashedCredentials: 从数据库中获取的密码
* credentialsSalt:密码加密的盐值
* RealmName: 类名(ShiroRealm)
*/
ByteSource credentialsSalt = ByteSource.Util.bytes("JQSalt");
AuthenticationInfo info = new SimpleAuthenticationInfo(user, user.getPazzword(), credentialsSalt, getName());
return info;
}
/**
* 在 shiro 中专门做授权认证的方法
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
//1 从参数 principals 中获取当前登录成功后的用户信息
User user = principals.oneByType(User.class);
//2 根据第一步中的用户信息,获取角色信息(若用户信息包含角色/权限信息,直接取出,若没有,从数据库中获取)
Set roles = RoleMapper.getRolesByUserid(user.getId());
// 通过用户关联的role信息,获取角色关联的 permisssion信息
Set permissions = permissionMapper.getPermissionsByUserid(user.getId());
Set newPermissions = new HashSet();
for (String permission : permissions) {
if(permission.contains("p:")) {
//删除约定前缀
newPermissions.add(permission.replaceAll("p:", ""));
}else {
newPermissions.add(permission);
}
}
//3 把获取到的登录用户关联的角色和权限资源信息注入到返回的SimpleAuthorizationInfo对象中
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addRoles(roles);
info.addStringPermissions(newPermissions);
return info;
}
}
3、登录访问项目:
结果和分析一致
参考文章: spring工厂方式创建bean实例
end ~