根据前面的了解,这次把基于权限的授权通过从数据库中初始化授权实现。
1、分析一下 Shiro 过滤链
<!-- 配置shiro的一些拦截规则,id必须和web.xml中的 shiro 拦截器名一致 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!-- Shiro的核心安全接口,这个属性是必须的 -->
<property name="securityManager" ref="securityManager" />
<!-- 身份认证失败,则跳转到登录页面的配置 -->
<property name="loginUrl" value="/login" />
<!-- 登录成功后的页面 -->
<property name="successUrl" value="/admin/index" />
<!-- 权限认证失败,则跳转到指定页面 -->
<property name="unauthorizedUrl" value="/unauthorized" /> <!-- 登录后访问没有权限的页面后跳转的页面 -->
<!-- Shiro连接约束配置,即过滤链的定义 -->
<property name="filterChainDefinitions">
<value>
<!-- 注意:规则是有顺序的,从上到下,拦截范围必须是从小到大的
url = 拦截规则(anon为匿名,authc为要登录后,才能访问,logout登出过滤) -->
/login = anon
/logout = logout
/admin/userlist = perms[userlist]
/admin/addUser = perms[addUser]
/admin/** = authc
/**= anon
</value>
</property>
</bean>
点击 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
<!-- 配置shiro的一些拦截规则,id必须和web.xml中的 shiro 拦截器名一致 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!-- Shiro的核心安全接口,这个属性是必须的 -->
<property name="securityManager" ref="securityManager" />
<!-- 身份认证失败,则跳转到登录页面的配置 -->
<property name="loginUrl" value="/login" />
<!-- 登录成功后的页面 -->
<property name="successUrl" value="/admin/index" />
<!-- 权限认证失败,则跳转到指定页面 -->
<property name="unauthorizedUrl" value="/unauthorized" /> <!-- 登录后访问没有权限的页面后跳转的页面 -->
<property name="filterChainDefinitionMap" ref="filterChainDefinitionMap"></property>
<!-- Shiro连接约束配置,即过滤链的定义 -->
<!-- <property name="filterChainDefinitions">
<value>
注意:规则是有顺序的,从上到下,拦截范围必须是从小到大的
url = 拦截规则(anon为匿名,authc为要登录后,才能访问,logout登出过滤)
/login = anon
/logout = logout
/admin/userlist = perms[userlist]
/admin/addUser = perms[addUser]
/admin/** = authc
/**= anon
</value>
</property> -->
</bean>
<!-- 通过实例工厂模式的bean配置方式:配置一个bean注入是一个Map实例 -->
<bean id="filterChainDefinitionMap" factory-bean="filterChainDefinitionMapFactory" factory-method="getFilterChainDefinitionMap"></bean>
<bean id="filterChainDefinitionMapFactory" class="cn.jq.ssm.service.shiro.FilterChainDefinitionMapFactory"></bean>
2)创建这个bean
import cn.jq.ssm.dao.PermissionMapper;
import cn.jq.ssm.model.Permission;
public class FilterChainDefinitionMapFactory {
@Autowired
private PermissionMapper permissionMapper;
public Map<String, String> getFilterChainDefinitionMap(){
//从数据库中获取数据
List<Permission> permissions = permissionMapper.getAllPermissions();
LinkedHashMap<String, String> 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 id="getAllPermissions" resultType="cn.jq.ssm.model.Permission">
select
p.id,p.pname,p.url
from
t_permission p
</select>
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<String> roles = RoleMapper.getRolesByUserid(user.getId());
// 通过用户关联的role信息,获取角色关联的 permisssion信息
Set<String> permissions = permissionMapper.getPermissionsByUserid(user.getId());
Set<String> 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 ~