项目中使用shiro @RequiresPermissions:注解 为登录用户授予权限

在这之前,Shiro的具体验证过程 链接:
https://blog.csdn.net/qq_37767455/article/details/99771312

https://blog.csdn.net/qq_37767455/article/details/91351791

下面完整代码地址:
https://gitee.com/never_enough/jxc_system

一、表设计

t_user:
项目中使用shiro @RequiresPermissions:注解 为登录用户授予权限_第1张图片
t_role:
项目中使用shiro @RequiresPermissions:注解 为登录用户授予权限_第2张图片
t_user_role:
项目中使用shiro @RequiresPermissions:注解 为登录用户授予权限_第3张图片
管理员role_id 为1,管理员拥有最高权限,即所有菜单,下面是t_role_menu:
项目中使用shiro @RequiresPermissions:注解 为登录用户授予权限_第4张图片
各表之间关联:
项目中使用shiro @RequiresPermissions:注解 为登录用户授予权限_第5张图片

二、登录 加载角色对应的菜单

若当前登录用户拥有一个角色,那么登录成功直接跳到主界面,否则让先让用户选择一个角色,然后再进入主界面,当然主界面的菜单是根据选中的角色来加载的。

项目中使用shiro @RequiresPermissions:注解 为登录用户授予权限_第6张图片
1.用户通过shiro验证后,也即是登录合法,然后将currentUser和用户拥有的角色存起来:
项目中使用shiro @RequiresPermissions:注解 为登录用户授予权限_第7张图片
2.回到登录页面 判断角色个数

项目中使用shiro @RequiresPermissions:注解 为登录用户授予权限_第8张图片
如果角色个数大于1,提示用户选择一个角色:
项目中使用shiro @RequiresPermissions:注解 为登录用户授予权限_第9张图片
点击提交:

项目中使用shiro @RequiresPermissions:注解 为登录用户授予权限_第10张图片
转到后台:
项目中使用shiro @RequiresPermissions:注解 为登录用户授予权限_第11张图片
3.进入主界面前请求加载菜单树:
项目中使用shiro @RequiresPermissions:注解 为登录用户授予权限_第12张图片
项目中使用shiro @RequiresPermissions:注解 为登录用户授予权限_第13张图片

项目中使用shiro @RequiresPermissions:注解 为登录用户授予权限_第14张图片

/**
	 *     这里获取的是一层的菜单
	 * 根据父节点和用户角色Id查询菜单
	 * @param parentId
	 * @param roleId
	 * @return
	 */
	public JsonArray getMenuByParentId(Integer parentId,Integer roleId) {
		List<Menu> menuList=menuService.findByParentIdAndRoleId(parentId, roleId);	
		JsonArray jsonArray=new JsonArray();
		for(Menu menu:menuList) {
			JsonObject jsonObject=new JsonObject();
			jsonObject.addProperty("id", menu.getId());//节点id  作为标识
			jsonObject.addProperty("text", menu.getName());//节点名字
			if(menu.getState()==1) {
				jsonObject.addProperty("state", "closed"); //状态为1说明是根结点
			}else {
				jsonObject.addProperty("state", "open");//叶子结点
			}
			
			jsonObject.addProperty("iconCls", menu.getIcon());//节点图标
			
			JsonObject attributeObject=new JsonObject(); //扩展属性
			attributeObject.addProperty("url", menu.getUrl());// 菜单请求地址
			
			jsonObject.add("attributes", attributeObject);
			jsonArray.add(jsonObject);
		}
		return jsonArray;
	}
	

持久层:

/**
	 * 根据父节点以及用户角色id查询子节点
	 * @param id
	 * @return
	 */
	@Query(value="SELECT * FROM t_menu WHERE p_id=?1 AND id IN (SELECT menu_id FROM t_role_menu WHERE role_id=?2)",nativeQuery=true)
	public List<Menu> findByParentIdAndRoleId(int parentId,int roleId);
	

界面展示效果:

项目中使用shiro @RequiresPermissions:注解 为登录用户授予权限_第15张图片
项目中使用shiro @RequiresPermissions:注解 为登录用户授予权限_第16张图片

项目中使用shiro @RequiresPermissions:注解 为登录用户授予权限_第17张图片

项目中使用shiro @RequiresPermissions:注解 为登录用户授予权限_第18张图片
由于当前登录用户是tony,选的 角色是总经理,角色对应的权限不多,所以菜单就展示了这么多。

三、为controller中的方法加上shiro注解,也即是访问权限

当点击一个菜单:
项目中使用shiro @RequiresPermissions:注解 为登录用户授予权限_第19张图片

在这里插入图片描述
发出请求获取客户列表:
项目中使用shiro @RequiresPermissions:注解 为登录用户授予权限_第20张图片
下面重点:
项目中使用shiro @RequiresPermissions:注解 为登录用户授予权限_第21张图片
由于请求要访问的方法上面加上了@RequiresPermissions注解,程序要执行到下面这里:
项目中使用shiro @RequiresPermissions:注解 为登录用户授予权限_第22张图片
用户登录进来 访问带shiro注解的方法时 要到这里 进行授权(给当前主体授权)

项目中使用shiro @RequiresPermissions:注解 为登录用户授予权限_第23张图片
给用户授予了 “客户管理 ”这个菜单权限后,才能正常访问:
项目中使用shiro @RequiresPermissions:注解 为登录用户授予权限_第24张图片
项目中使用shiro @RequiresPermissions:注解 为登录用户授予权限_第25张图片

四、部分代码展示

package com.java1234.realm;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.annotation.Resource;

import org.apache.shiro.SecurityUtils;
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.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;

import com.java1234.entity.Menu;
import com.java1234.entity.Role;
import com.java1234.entity.User;
import com.java1234.repository.MenuRepository;
import com.java1234.repository.RoleRepository;
import com.java1234.repository.UserRepository;

/**
 * 自定义Realm
 * @author Administrator
 *
 */
public class MyRealm extends AuthorizingRealm{

	@Resource
	private UserRepository userRepository;
	
	@Resource
	private RoleRepository roleRepository;
	
	@Resource
	private MenuRepository menuRepository;
	
	
	/**
	 * 授权  :是什么用户就给他什么角色   什么菜单权限
	 * 用户登录进来 访问带shiro注解的方法时 要到这里  进行授权(给当前主体授权)
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		String userName=(String) SecurityUtils.getSubject().getPrincipal();
		User user=userRepository.findByUserName(userName);
		
		SimpleAuthorizationInfo  info=new SimpleAuthorizationInfo();
		
		List<Role> roleList=roleRepository.findByUserId(user.getId());
		Set<String> roles=new HashSet<String>();
		for(Role role:roleList) {
			roles.add(role.getName());
			
			List<Menu> menuList=menuRepository.findByRoleId(role.getId());
			for(Menu menu:menuList) {
				info.addStringPermission(menu.getName());
			}
		}
		info.setRoles(roles);
		
		return info;
	}


	/**
	 * 身份认证:登录的时候判断权限   是否有权限       判断用户名和密码
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		String userName=(String) token.getPrincipal();
		User user=userRepository.findByUserName(userName); //获取首体、当事人
		if(user!=null) {    //封装成AuthenticationInfo然后返回  它内部进行判断密码是否正确(输入的用户名对应的用户存在,继续判断该用户密码是否正确)
			AuthenticationInfo authcInfo=new SimpleAuthenticationInfo(user.getUserName(), user.getPassword(),"xxx");
			return authcInfo;
		}else {
			return null; //用户不存在  return Null
		}
	}
	
}

package com.java1234.config;

import java.util.LinkedHashMap;
import java.util.Map;

import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;

import com.java1234.realm.MyRealm;

/**
 * Shiro配置类
 */
@Configuration
public class ShiroConfig {

	/**
     * ShiroFilterFactoryBean 处理拦截资源文件问题。
     * 注意:单独一个ShiroFilterFactoryBean配置是会报错的,因为在
     * 初始化ShiroFilterFactoryBean的时候需要注入:SecurityManager
     *
     * Filter Chain定义说明 1、一个URL可以配置多个Filter,使用逗号分隔 2、当设置多个过滤器时,全部验证通过,才视为通过
     * 3、部分过滤器可指定参数,如perms,roles
     *
     */
    @Bean
    public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

        // 必须设置 SecurityManager
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        //设置登录界面 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
        shiroFilterFactoryBean.setLoginUrl("/login.html");
 

        // 拦截器.
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
        // 配置不会被拦截的链接 顺序判断
        filterChainDefinitionMap.put("/static/**", "anon");
        filterChainDefinitionMap.put("/user/login", "anon");
        filterChainDefinitionMap.put("/drawImage", "anon");//验证码生成的请求也不需要验证即可访问

        // 配置退出过滤器,其中的具体的退出代码Shiro已经替我们实现了
        /*
         * 退出后 shiro内置currentUser 被自动清掉 但我们自己设置的currentUser还在
         * 因此还需在 controller的logout方法中手动清理
         */
        filterChainDefinitionMap.put("/logout", "logout");


        // :这是一个坑呢,一不小心代码就不好使了;
        // 
        filterChainDefinitionMap.put("/**", "authc");//除了上面那些,其他的请求都需要认证

        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 设置realm.
        securityManager.setRealm(myRealm());
        return securityManager;
    }

    /**
     * 身份认证realm; (这个需要自己写,账号密码校验;权限等)
     * 
     * @return
     */
    @Bean        //返回一个Bean
    public MyRealm myRealm() {
    	MyRealm myRealm = new MyRealm();
        return myRealm;
    }

    /**
     * Shiro生命周期处理器     (固定写法)
     * @return
     */
    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){
        return new LifecycleBeanPostProcessor();
    }
    
    /**
     * 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证
     * 配置以下两个bean(DefaultAdvisorAutoProxyCreator(可选)和AuthorizationAttributeSourceAdvisor)即可实现此功能
     * @return
     * 
     * 
     * Controller里面有方法,方法里面要进行权限认证
     * @RequiresPermissions:比如需要有自己的菜单权限,否则访问会报错
     */
    @Bean
    @DependsOn({"lifecycleBeanPostProcessor"})
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(){
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());
        return authorizationAttributeSourceAdvisor;
    }

}

你可能感兴趣的:(shiro)