Shiro 从数据库中初始化授权

根据前面的了解,这次把基于权限的授权通过从数据库中初始化授权实现。

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 ~

猜你喜欢

转载自blog.csdn.net/qq_42402854/article/details/89531991