SpringMVC集成shiro

5 篇文章 0 订阅
2 篇文章 0 订阅

最近公司一个老的后台管理系统需要加权限验证(前端使用easyUI),经过各种踩坑,终于是完成了,话不多说,直接进入主题
数据库结构
用户表(使用的项目本身已经在用的表)

CREATE TABLE `pay_user` (
	`ID` INT(11) NOT NULL AUTO_INCREMENT COMMENT 'ID主键',
	`USER_NAME` VARCHAR(45) NOT NULL COMMENT '登录名称',
	`PASSWORD` VARCHAR(50) NOT NULL COMMENT '密码',
	`STATUS` VARCHAR(1) NOT NULL DEFAULT '0' COMMENT '状态:0:正常,1:禁用;default:0',
	`CREATE_TIME` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
	`UPDATE_TIME` DATETIME NULL DEFAULT NULL COMMENT '更新时间',
	PRIMARY KEY (`ID`),
	UNIQUE INDEX `ID_UNIQUE` (`ID`) USING BTREE,
	UNIQUE INDEX `USER_NAME_UK` (`USER_NAME`) USING BTREE
)
COMMENT='运营管理用户表'
COLLATE='utf8_general_ci'
ENGINE=InnoDB
AUTO_INCREMENT=48
;

菜单表

CREATE TABLE `sys_menu` (
	`id` BIGINT(8) NOT NULL AUTO_INCREMENT COMMENT '主键',
	`name` VARCHAR(64) NOT NULL COMMENT '菜单名称',
	`icon` VARCHAR(64) NOT NULL DEFAULT 'icon-nav' COMMENT '图标',
	`url` VARCHAR(255) NULL DEFAULT NULL COMMENT '菜单路径',
	`pid` BIGINT(8) UNSIGNED NOT NULL DEFAULT '0' COMMENT '父级资源id',
	`sort` TINYINT(2) NOT NULL DEFAULT '0' COMMENT '排序',
	`permission_code` VARCHAR(50) NULL DEFAULT NULL COMMENT '权限层级',
	`status` TINYINT(2) NOT NULL DEFAULT '0' COMMENT '状态',
	`menu_flag` TINYINT(1) NULL DEFAULT '0' COMMENT '是否是菜单 0主菜单 1页面 2按钮',
	`description` VARCHAR(255) NULL DEFAULT NULL COMMENT '备注',
	`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
	`update_time` DATETIME NULL DEFAULT NULL COMMENT '更新时间',
	PRIMARY KEY (`id`)
)
COMMENT='菜单'
COLLATE='utf8_general_ci'
ENGINE=InnoDB
AUTO_INCREMENT=297
;

角色表(这里当时有个设计缺陷就是应该多加一个code字段)

CREATE TABLE `sys_role` (
	`id` BIGINT(19) NOT NULL AUTO_INCREMENT COMMENT '主键id',
	`name` VARCHAR(64) NOT NULL COMMENT '角色名',
	`sort` TINYINT(2) NOT NULL DEFAULT '0' COMMENT '排序号',
	`description` VARCHAR(255) NULL DEFAULT NULL COMMENT '描述',
	`create_time` DATETIME NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
	`update_time` DATETIME NULL DEFAULT NULL COMMENT '更新时间',
	PRIMARY KEY (`id`)
)
COMMENT='角色'
COLLATE='utf8_general_ci'
ENGINE=InnoDB
AUTO_INCREMENT=7
;

角色-菜单关系表

CREATE TABLE `sys_role_menu` (
	`id` BIGINT(8) NOT NULL AUTO_INCREMENT COMMENT '主键id',
	`role_id` BIGINT(8) NOT NULL COMMENT '角色id',
	`menu_id` BIGINT(8) NOT NULL COMMENT '菜单id',
	PRIMARY KEY (`id`)
)
COMMENT='角色-菜单关系表'
COLLATE='utf8_general_ci'
ENGINE=InnoDB
AUTO_INCREMENT=2763
;

用户-角色关系表

CREATE TABLE `sys_user_role` (
	`id` BIGINT(8) NOT NULL AUTO_INCREMENT COMMENT '主键id',
	`user_id` BIGINT(8) NOT NULL COMMENT '用户id',
	`role_id` BIGINT(8) NOT NULL COMMENT '角色id',
	PRIMARY KEY (`id`)
)
COMMENT='用户-角色关系表'
COLLATE='utf8_general_ci'
ENGINE=InnoDB
AUTO_INCREMENT=28
;

1.首先引入依赖

 <!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-web -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-web</artifactId>
            <version>1.4.0</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-spring -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.4.0</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-ehcache -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-ehcache</artifactId>
            <version>1.4.0</version>
        </dependency>

        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <version>1.4.0</version>
        </dependency>

当然像ehcache如果用不到可以不添加

2.web.xml配置
添加配置

 <!--&lt;!&ndash;Shiro配置 DelegatingFilterProxy通过代理模式将spring容器中的bean和filter关联起来&ndash;&gt;-->
	<filter>
		<filter-name>shiroFilter</filter-name>
		<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
		<!-- 设置true由servlet容器控制filter的生命周期 -->
		<init-param>
			<param-name>targetFilterLifecycle</param-name>
			<param-value>true</param-value>
		</init-param>
	</filter>
	<filter-mapping>
		<filter-name>shiroFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

3.spring-shiro.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
       xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
	http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
	http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
	http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd">

    <!-- shiro -->
    <!-- 1. 将Shiro的生命周期加入Spring
    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
    -->

    <!-- 凭证匹配器 -->
    <bean id="credentialsMatcher"
          class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
        <property name="hashAlgorithmName" value="md5"/>
        <property name="hashIterations" value="1"/>
    </bean>

    <!-- 2. 自定义Realm来管理权限 -->
    <bean id="shiroDbRealm" class="com.joiest.jpf.manage.web.shiro.ShiroDbRealm">
        <property name="credentialsMatcher" ref="credentialsMatcher"/>
    </bean>

    <!--&lt;!&ndash; 缓存管理器 &ndash;&gt;-->
    <!--<bean id="cacheMnager" class="org.apache.shiro.cache.ehcache.EhCacheManager">-->
        <!--<property name="cacheManagerConfigFile" value="classpath:spring/shiro-ehcache.xml"/>-->
    <!--</bean>-->

    <!--&lt;!&ndash; 会话管理器 &ndash;&gt;-->
    <!--<bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">-->
        <!--&lt;!&ndash; session的失效时长,单位毫秒 &ndash;&gt;-->
        <!--<property name="globalSessionTimeout" value="1800000"/>-->
        <!--&lt;!&ndash; 删除失效的session &ndash;&gt;-->
        <!--<property name="deleteInvalidSessions" value="true"/>-->
    <!--</bean>-->

    <!-- 3. 将自定义Realm赋值给securityManager -->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="realm" ref="shiroDbRealm"/>
        <!--<property name="sessionManager" ref="sessionManager" />-->
        <!--<property name="cacheManager" ref="cacheMnager" />-->
    </bean>

    <!-- 4. 配置SecurityUtils将securityManager-->
    <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
        <property name="staticMethod" value="org.apache.shiro.SecurityUtils.setSecurityManager"/>
        <property name="arguments" ref="securityManager"/>
    </bean>

    <!-- 5. 配置shiroFilter-->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <!-- Shiro的核心安全接口,这个属性是必须的 -->
        <property name="securityManager" ref="securityManager"/>
        <!-- 要求登录时的链接(登录页面地址),非必须的属性,默认会自动寻找Web工程根目录下的"/login.jsp"页面 -->
        <property name="loginUrl" value="/"/>
        <!-- 登录成功后要跳转的连接(本例中此属性用不到,因为登录成功后的处理逻辑在页面处理的) -->
        <property name="successUrl" value="/backIndex.jsp" ></property>
        <!-- 用户访问未对其授权的资源时,所显示的连接 -->
        <property name="unauthorizedUrl" value="/refuse.jsp"></property>
        <!-- 过虑器链定义,从上向下顺序执行,一般将/**放在最下边 -->
        <property name="filterChainDefinitionMap"  ref="filterChainDefinitionMap">
        </property>
        
       <property name="filterChainDefinitions">
            <value>
                <!-- anon不需要认证-->
                <!--/login = anon-->
                /resources/** = anon
                /user/login = anon
                /user/logout = anon

				 <!--这里可以控制地址栏输入url也进行拦截,但是这样配置太繁琐,下篇文章会扩展-->
				/user/index = perms["userManage:all"]
                
                <!--/user/index = anon-->
                <!--所有的请求(除去配置的静态资源请求或请求地址为anon的请求)都要通过登录验证,如果未登录则跳到/login -->
                /** = authc

            </value>
        </property>
    </bean>
</beans>

4.创建自定义Realm

package com.joiest.jpf.manage.web.shiro;

import com.joiest.jpf.common.po.SysMenu;
import com.joiest.jpf.common.po.SysRoleMenu;
import com.joiest.jpf.common.po.SysUserRole;
import com.joiest.jpf.common.util.Md5Encrypt;
import com.joiest.jpf.common.util.SHA1;
import com.joiest.jpf.entity.UserInfo;
import com.joiest.jpf.facade.SysMenuFacade;
import com.joiest.jpf.facade.SysRoleMenuFacade;
import com.joiest.jpf.facade.SysUserRoleFacade;
import com.joiest.jpf.facade.UserServiceFacade;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authc.*;
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 org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;

public class ShiroDbRealm extends AuthorizingRealm {
    @Autowired
    private UserServiceFacade userServiceFacade;
    @Autowired
    private SysUserRoleFacade sysUserRoleFacade;
    @Autowired
    private SysRoleMenuFacade sysRoleMenuFacade;
    @Autowired
    private SysMenuFacade sysMenuFacade;

    //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("=================doGetAuthenticationInfo[登录认证]=====================");
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        System.out.println("当前认证用户:" + token.getUsername());
        UserInfo user = userServiceFacade.getByLoginName(token.getUsername());
        if (user != null) {
            ShiroUser shiroUser = new ShiroUser(user.getUserName());
            shiroUser.setId(user.getId()+"");
            shiroUser.setName(user.getUserName());
            //以创建时间作为盐(加密用的)
//            String salt = String.valueOf(user.getCreateTime().getTime());
            return new SimpleAuthenticationInfo(shiroUser, Md5Encrypt.md5(user.getPassword()), getName());
        }
        return null;
    }

    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("=================doGetAuthorizationInfo[授权认证]=====================");
        ShiroUser shiroUser = (ShiroUser) principalCollection.getPrimaryPrincipal();

        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        List<String> permissions = new ArrayList<>();
        //由于炒鸡管理员权限太多,加载每次点击菜单加载数据很慢,所以这里用了缓存,只第一次加载
        if (shiroUser.getPermissions()==null || shiroUser.getPermissions().size() == 0){
            //获取所有权限
            List<SysUserRole> sysUserRoles =sysUserRoleFacade.selectRolesByUserId(shiroUser.getId());
            for (SysUserRole sysUserRole : sysUserRoles) {
                List<SysRoleMenu> sysRoleMenus = sysRoleMenuFacade.getRoleMenuByRoleId(sysUserRole.getRoleId());
                if (sysRoleMenus.size()>0){
                    for (SysRoleMenu sysRoleMenu : sysRoleMenus) {
                        SysMenu menu = sysMenuFacade.getMenuById(sysRoleMenu.getMenuId());
                        if(menu!=null&& StringUtils.isNotBlank(menu.getPermissionCode())){
                            permissions.add(menu.getPermissionCode());
                        }
                    }
                }

            }

            shiroUser.setPermissions(new HashSet<>(permissions));
            info.addStringPermissions(permissions);

        }else{
            //info里存的就是所有的权限
            info.addStringPermissions(shiroUser.getPermissions());
        }


        return info;
    }

}

5.User类 shiroUser

package com.joiest.jpf.manage.web.shiro;

import java.io.Serializable;
import java.util.Set;

public class ShiroUser implements Serializable {

    private String id;
    private final String loginName;
    private String name;
    private Set<String> permissions;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public ShiroUser(String loginName) {
        this.loginName = loginName;
    }

    public String getLoginName() {
        return loginName;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Set<String> getPermissions() {
        return permissions;
    }

    public void setPermissions(Set<String> permissions) {
        this.permissions = permissions;
    }
}

6.Tree

package com.joiest.jpf.entity;

import java.io.Serializable;
import java.util.Date;
import java.util.List;

public class Tree implements Serializable {
    private Integer id;
    private String text;
    private int seq;
    private boolean checked = false;// true,false
    private boolean onlyLeafCheck = false;
    private String state = "open";
    private String code;
    private List<Tree> children;
    private String iconCls;
    private Integer pid;
    private Integer isLeaf;
    private String url;
    private String createTime;
    private String updateTime;

        public String getState() {
        return state;
    }

    public void setState(String state) {
        this.state = state;
    }

    public boolean getOnlyLeafCheck() {
        return onlyLeafCheck;
    }

    public void setOnlyLeafCheck(boolean onlyLeafCheck) {
        this.onlyLeafCheck = onlyLeafCheck;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getText() {
        return text;
    }

    public void setText(String text) {
        this.text = text;
    }

    public boolean getChecked() {
        return checked;
    }

    public void setChecked(boolean checked) {
        this.checked = checked;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public List<Tree> getChildren() {
        return children;
    }

    public void setChildren(List<Tree> children) {
        this.children = children;
    }

    public String getIconCls() {
        return iconCls;
    }

    public void setIconCls(String iconCls) {
        this.iconCls = iconCls;
    }

    public Integer getPid() {
        return pid;
    }

    public void setPid(Integer pid) {
        this.pid = pid;
    }

    public Integer getIsLeaf() {
        return isLeaf;
    }

    public void setIsLeaf(Integer isLeaf) {
        this.isLeaf = isLeaf;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public int getSeq() {
        return seq;
    }

    public void setSeq(int seq) {
        this.seq = seq;
    }

    public String getCreateTime() {
        return createTime;
    }

    public void setCreateTime(String createTime) {
        this.createTime = createTime;
    }

    public String getUpdateTime() {
        return updateTime;
    }

    public void setUpdateTime(String updateTime) {
        this.updateTime = updateTime;
    }
}

7.登录 调用shiro的login()方法

@RequestMapping(value = "/user/login", method = RequestMethod.POST)
	public ModelAndView login(HttpServletRequest request,HttpSession httpSession, Model model) {
		String loginName = request.getParameter("user");
		String password = request.getParameter("pwd");
		Subject user = SecurityUtils.getSubject();
		UsernamePasswordToken token = new UsernamePasswordToken(loginName, SHA1.getInstance().getMySHA1Code(password));

		if (StringUtils.isBlank(loginName)||StringUtils.isBlank(password)) {
			model.addAttribute("error", "用户名或密码错误");
			return new ModelAndView("login");
		}

		UserInfo info = userServiceFacade.getByLoginName(loginName);
		if (info == null){
			model.addAttribute("error", "用户不存在或者密码错误!");
			return new ModelAndView("login");
		}

		if (!EnumConstants.UserStatus.normal.value().equals(info.getStatus())){
			model.addAttribute("error", "用户已禁用!");
			return new ModelAndView("login");
		}

		List<SysUserRole> userRoles = sysUserRoleFacade.selectRolesByUserId(info.getId() + "");
		if (userRoles.size() == 0){
			model.addAttribute("error", "用户未分配角色,请联系管理员!");
			return new ModelAndView("login");
		}

		try {
		    //shiro的login()方法
			user.login(token);
		} catch (UnknownAccountException  e) {
			model.addAttribute("error", "用户不存在或者密码错误!");
			return new ModelAndView("login");
		} catch (IncorrectCredentialsException ex) {
			model.addAttribute("error","用户不存在或者密码错误!") ;
			return new ModelAndView("login");
		} catch (AuthenticationException ex) {
			model.addAttribute("error","用户名或密码错误");
			return new ModelAndView("login");
		} catch (Exception ex) {
			model.addAttribute("error","内部错误,请重试!");
			return new ModelAndView("login");
		}

		PayUser userInfo = userServiceFacade.loginVerify(loginName, password);
		
		//登录信息存进session
        httpSession.setAttribute(ManageConstants.USERINFO_SESSION,userInfo);
        httpSession.setMaxInactiveInterval(30*60);//以秒为单位,即在没有活动30分钟后,session将失效
		return new ModelAndView("/backIndex");
	}

8.跳转到首页后发送ajax请求(/menu)查询当前用户下的所有菜单

controller

@RequestMapping("/menu")
	@ResponseBody
	public Object getMenu(){
		UserInfo sessionUser = SessionUtil.getSessionUser();
		Integer id = sessionUser.getId();

		List<Tree> treeList = sysMenuFacade.treeList(id);
		JSONArray jsonArray = JSONArray.parseArray(JSON.toJSONString(treeList));

		return jsonArray;
	}

serviceImpl

 @Override
    public List<Tree> treeList(Integer id) {
        List<SysMenu> menuList = sysMenuMapper.selectMenuListByUserId(id);
        List<Tree> treeList = prepareTree(menuList);
        //排序调整菜单顺序
        Collections.sort(treeList, new Comparator<Tree>() {
            @Override
            public int compare(Tree t1, Tree t2) {
                return t1.getSeq() - t2.getSeq();
            }
        });

        return treeList;
    }

private List<Tree> prepareTree(List<SysMenu> menuList) {
        List<Tree> allTreeList = menuListToTreeList(menuList);
        List<Tree> topList = new ArrayList<>();
        for (Tree tree : allTreeList) {
            // 遍历所有节点,将父菜单id与传过来的id比较
            if (tree.getPid() == 0) {
                tree.setChildren(prepareTreeChiled(tree.getId(), allTreeList));
                topList.add(tree);
                Collections.sort(topList, new Comparator<Tree>() {
                    @Override
                    public int compare(Tree o1, Tree o2) {
                        return o1.getSeq() - o2.getSeq();
                    }
                });
            }
        }
        return topList;
    }

    private List<Tree> prepareTreeChiled(Integer id, List<Tree> allTreeList) {
        // 子菜单
        List<Tree> childList = new ArrayList<>();
        for (Tree tree : allTreeList) {
            // 遍历所有节点,将父菜单id与传过来的id比较
             //这里有个坑 因为常量池里127 == 127是成立的  129 == 129不成立 所以比较用equals
            if (!"0".equals(tree.getPid()+"") && (id+"").equals(tree.getPid()+"")) {
                childList.add(tree);
                Collections.sort(childList, new Comparator<Tree>() {
                    @Override
                    public int compare(Tree o1, Tree o2) {
                        return o1.getSeq() - o2.getSeq();
                    }
                });
            }
        }
        // 把子菜单的子菜单再循环一遍
        for (Tree tree : childList) {
                tree.setChildren(prepareTreeChiled(tree.getId(), allTreeList));
        }
        // 递归退出条件
        if (childList.size() == 0) {
            return null;
        }
        return childList;
    }

    private List<Tree> menuListToTreeList(List<SysMenu> menuList) {
        List<Tree> treeList = new ArrayList<>();
        if (menuList != null && menuList.size() > 0) {
            for (SysMenu menu : menuList) {
                treeList.add(menuToTree(menu));
            }
        }
        return treeList;
    }

    private Tree menuToTree(SysMenu menu) {
        Tree tree = new Tree();
        tree.setId(Integer.valueOf(menu.getId()));
        tree.setText(menu.getName());
        tree.setIconCls(menu.getIcon());
        tree.setIsLeaf(Integer.valueOf(menu.getMenuFlag()));
        tree.setPid(Integer.valueOf(menu.getPid()));
        tree.setSeq(menu.getSort());
        tree.setUrl(menu.getUrl());
        tree.setCode(menu.getPermissionCode());
        tree.setChecked(menu.getChecked());


        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String createTime = formatter.format(menu.getCreateTime());
        if (menu.getUpdateTime() != null){
            String updateTime = formatter.format(menu.getUpdateTime());
            tree.setUpdateTime(updateTime);
        }

        tree.setCreateTime(createTime);

        return tree;
    }


selectMenuListByUserId sql

<select id="selectMenuListByUserId" resultMap="BaseResultMap">
    select distinct m.id,m.name,m.icon,m.url,m.pid,m.sort,m.permission_code,m.`status`,m.menu_flag,
    m.description,m.create_time,m.update_time
    from sys_menu m
    left join sys_role_menu rm
    on m.id = rm.menu_id
    left join sys_role r
    on r.id = rm.role_id
    left join sys_user_role ur
    on ur.role_id = r.id
    left join pay_user u
    on u.id = ur.user_id
    where u.id = #{id}
    and m.menu_flag != 2
  </select>

8.页面配置
//menu:add 是你表中自定义的权限code
<shiro:hasPermission name=“menu:add”>
//这里是你要控制的按钮代码
</shiro:hasPermission>

这样只要你的用户下有menu:add这个权限,就可以看到被shiro标签包含的按钮,反之则看不到

9.至于上面配置文件中提到的:
/user/index = perms[“userManage:all”]
这个是用来防止有人直接在地址栏输入地址的,你可以尝试一下,一个用户没有某个页面的权限,但是直接地址栏输入地址还是可以访问的,只不过没有权限的按钮不会显示出来,但这样明显是不可行的,所以每多一个页面都需要在配置文件配置这个参数,这样很繁琐,下一篇文章会介绍一种自定义filterChainDefinitionMap的方式

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值