SpringBoot整合Shiro

本文详细介绍了如何在SpringBoot项目中整合Shiro进行权限管理,包括搭建基础工程、配置Shiro、实现身份校验和角色设置,以及实现精确到按钮级别的权限控制。

因为工作原因,项目中使用了Springboot 整合Shiro来做权限的整合,项目接近尾声,我也按照网上的资料自己搭了一个,踩了一些坑。下面是步骤

目录

(一) 基础工程

1.1 首先是搭建一个springboot 的web项目 这个就不多说了

1.2 依赖引入

1.3 这里我们用到thymeleaf模版

1.4 创建页面

1.5 在entity包下把实体类创建起来。

(二)身份校验和角色设置

2.1 数据库中插入数据

2.2 .Realms

2.3 service方法实现

2.4 dao

2.5 接下来配置Shiro的关键部分

2.6 修改我们的HomeController中的/login请求


 

(一) 基础工程

图片是项目搭建结构图

1.1 首先是搭建一个springboot 的web项目 这个就不多说了

1.2 依赖引入

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.bobby.springboot</groupId>
  <artifactId>boot_shrio</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>war</packaging>

  <name>boot_shrio Maven Webapp</name>
  <!-- FIXME change it to the project's website -->
  <url>http://www.example.com</url>

  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.0.4.RELEASE</version>
    <relativePath /> <!-- lookup parent from repository -->
  </parent>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <java.version>1.8</java.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter</artifactId>
    </dependency>

    <!--thymeleaf  模板-->
    <dependency>
      <groupId>net.sourceforge.nekohtml</groupId>
      <artifactId>nekohtml</artifactId>
      <version>1.9.22</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
    </dependency>
    <!--shiro   依赖-->
    <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-spring</artifactId>
      <version>1.4.0</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-devtools</artifactId>
      <optional>true</optional>
    </dependency>

    <!-- mp 依赖 -->
    <dependency>
      <groupId>com.baomidou</groupId>
      <artifactId>mybatis-plus</artifactId>
      <version>2.3</version>
    </dependency>

  </dependencies>
  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <configuration>
          <fork>true</fork>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

1.3 这里我们用到thymeleaf模版

application.yml:在这个地方我遇到了一个问题 就是我不指定templates目录,使用spriboot默认的static文件夹下,但访问不了。

已解决访问static中index文件夹下的index.html页面

spring:
    thymeleaf:
        suffix: .html
        encoding: UTF-8
        content-type: text/html
        mode: HTML5
        prefix: classpath:/templates/
    application:
        name: shiro
    datasource:
        url: jdbc:mysql://127.0.0.1:3306/bobby?useUnicode=true&characterEncoding=utf-8
        username: root
        password: 123456
        driver-class-name: com.mysql.jdbc.Driver
    jpa:
        database: mysql
        showSql: true
        hibernate:
            ddlAuto: update
        properties:
            hibernate:
                dialect: org.hibernate.dialect.MySQL5Dialect
                format_sql: true

1.4 创建页面

index.html、userAdd.html、userDel.html、userInfo.html中只需要显示一句简单的文字即可。

login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
错误信息:<h4 style="color: red;" th:text="${msg}"></h4>
<form th:action="@{/login}" method="post">
    <p>账号:<input type="text" name="username" value="admin"/></p>
    <p>密码:<input type="password" name="password" value="123456"/></p>
    <p><input type="submit" value="登录"/></p>
</form>
</body>
</html>

1.5 在entity包下把实体类创建起来。

SysPermission 

package com.bobby.springboot.entity;

/**
 * Created by Bobby on 2019/9/19 17:23
 */
import java.io.Serializable;
import java.util.List;

import javax.persistence.*;

@Entity
@Table(name = "sys_permission")
public class SysPermission implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id@GeneratedValue
    private long id;//主键.
    private String name;//名称.

    @Column(columnDefinition="enum('menu','button')")
    private String resourceType;//资源类型,[menu|button]
    private String url;//资源路径.
    private String permission; //权限字符串,menu例子:role:*,button例子:role:create,role:update,role:delete,role:view
    private Long parentId; //父编号
    private String parentIds; //父编号列表
    private Boolean available = Boolean.FALSE;

    @ManyToMany
    @JoinTable(name="SysRolePermission",joinColumns={@JoinColumn(name="permissionId")},inverseJoinColumns={@JoinColumn(name="roleId")})
    private List<SysRole> roles;

    public long getId() {
        return id;
    }
    public void setId(long id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getResourceType() {
        return resourceType;
    }
    public void setResourceType(String resourceType) {
        this.resourceType = resourceType;
    }
    public String getUrl() {
        return url;
    }
    public void setUrl(String url) {
        this.url = url;
    }
    public String getPermission() {
        return permission;
    }
    public void setPermission(String permission) {
        this.permission = permission;
    }
    public Long getParentId() {
        return parentId;
    }
    public void setParentId(Long parentId) {
        this.parentId = parentId;
    }
    public String getParentIds() {
        return parentIds;
    }
    public void setParentIds(String parentIds) {
        this.parentIds = parentIds;
    }
    public Boolean getAvailable() {
        return available;
    }
    public void setAvailable(Boolean available) {
        this.available = available;
    }
    public List<SysRole> getRoles() {
        return roles;
    }
    public void setRoles(List<SysRole> roles) {
        this.roles = roles;
    }
    @Override
    public String toString() {
        return "SysPermission [id=" + id + ", name=" + name + ", resourceType=" + resourceType + ", url=" + url
                + ", permission=" + permission + ", parentId=" + parentId + ", parentIds=" + parentIds + ", available="
                + available + ", roles=" + roles + "]";
    }
}


userinfo

package com.bobby.springboot.entity;

import javax.persistence.*;
import java.io.Serializable;
import java.util.List;

/**
 * Created by Bobby on 2019/9/23 11:13
 */
@Entity
@Table(name = "user_info")
public class UserInfo implements Serializable {


    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue
    private long uid;//用户id;

    @Column(unique=true)
    private String username;//账号.

    private String name;//名称(昵称或者真实姓名,不同系统不同定义)

    private String password; //密码;
    private String salt;//加密密码的盐

    private byte state;//用户状态,0:创建未认证(比如没有激活,没有输入验证码等等)--等待验证的用户 , 1:正常状态,2:用户被锁定.


    @ManyToMany(fetch= FetchType.EAGER)//立即从数据库中进行加载数据;
    @JoinTable(name = "SysUserRole", joinColumns = { @JoinColumn(name = "uid") }, inverseJoinColumns ={@JoinColumn(name = "roleId") })
    private List<SysRole> roleList;// 一个用户具有多个角色

    public List<SysRole> getRoleList() {
        return roleList;
    }

    public void setRoleList(List<SysRole> roleList) {
        this.roleList = roleList;
    }

    public long getUid() {
        return uid;
    }

    public void setUid(long uid) {
        this.uid = uid;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getName() {
        return name;
    }

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

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getSalt() {
        return salt;
    }

    public void setSalt(String salt) {
        this.salt = salt;
    }

    public byte getState() {
        return state;
    }

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

    /**
     * 密码盐.
     * @return
     */
    public String getCredentialsSalt(){
        return this.username+this.salt;
    }

    @Override
    public String toString() {
        return "UserInfo [uid=" + uid + ", username=" + username + ", name=" + name + ", password=" + password
                + ", salt=" + salt + ", state=" + state + "]";
    }
}

sysrole

package com.bobby.springboot.entity;

/**
 * Created by Bobby on 2019/9/19 17:23
 */
import java.io.Serializable;
import java.util.List;

import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Table;

@Entity
@Table(name = "sys_role")
public class SysRole implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id@GeneratedValue
    private Long id; // 编号
    private String role; // 角色标识程序中判断使用,如"admin",这个是唯一的:
    private String description; // 角色描述,UI界面显示使用
    private Boolean available = Boolean.FALSE; // 是否可用,如果不可用将不会添加给用户

    //角色 -- 权限关系:多对多关系;
    @ManyToMany(fetch= FetchType.EAGER)
    @JoinTable(name="SysRolePermission",joinColumns={@JoinColumn(name="roleId")},inverseJoinColumns={@JoinColumn(name="permissionId")})
    private List<SysPermission> permissions;

    // 用户 - 角色关系定义;
    @ManyToMany
    @JoinTable(name="SysUserRole",joinColumns={@JoinColumn(name="roleId")},inverseJoinColumns={@JoinColumn(name="uid")})
    private List<UserInfo> userInfos;// 一个角色对应多个用户

    public List<UserInfo> getUserInfos() {
        return userInfos;
    }
    public void setUserInfos(List<UserInfo> userInfos) {
        this.userInfos = userInfos;
    }
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getRole() {
        return role;
    }
    public void setRole(String role) {
        this.role = role;
    }
    public String getDescription() {
        return description;
    }
    public void setDescription(String description) {
        this.description = description;
    }
    public Boolean getAvailable() {
        return available;
    }
    public void setAvailable(Boolean available) {
        this.available = available;
    }
    public List<SysPermission> getPermissions() {
        return permissions;
    }
    public void setPermissions(List<SysPermission> permissions) {
        this.permissions = permissions;
    }
//    @Override
//    public String toString() {
//        return "SysRole [id=" + id + ", role=" + role + ", description=" + description + ", available=" + available
//                + ", permissions=" + permissions + "]";
//    }

}

 

 


@Controller
public class HomeController {
    @RequestMapping({"/","/index"})
    public String index() {
        return "index";
    }
    
    @RequestMapping("/login")
    public String login() {
        return "login";
    }
}

启动项目,数据库表会自动建好,index和login页面也可以访问。

(二)身份校验和角色设置

2.1 数据库中插入数据

INSERT INTO test.sys_permission VALUES ('1', 1, '用户管理', '0', '0/', 'userInfo:view', 'menu', 'userInfo/userList');
INSERT INTO test.sys_permission VALUES ('2', 1, '用户添加', '1', '0/1', 'userInfo:add', 'button', 'userInfo/userAdd');
INSERT INTO test.sys_permission VALUES ('3', 1, '用户删除', '1', '0/1', 'userInfo:del', 'button', 'userInfo/userDel');

INSERT INTO test.sys_role VALUES ('1', 1, '管理员', 'admin');
INSERT INTO test.sys_role VALUES ('2', 1, 'VIP会员', 'vip');

INSERT INTO test.user_info (uid,username,name,password,salt,state) VALUES ('1', 'admin','管理员' , 'd3c59d25033dbf980d29554025c23a75', '8d78869f470951332959580424d4bf4f', 0);

INSERT INTO test.sys_role_permission VALUES ('1', '1');
INSERT INTO test.sys_role_permission VALUES ('1', '2');

INSERT INTO test.sys_user_role VALUES ('1', '1');

2.2 .Realms

Realm是一个Dao,通过它来验证用户身份和权限。这里Shiro不做权限的管理工作,需要我们自己管理用户权限,只需要从我们的数据源中把用户和用户的角色权限信息取出来交给Shiro即可。
config包下再建一个包Shiro,然后在Shiro包下建一个MyShiroRealm类,继承AuthorizingRealm抽象类。

package com.bobby.springboot.config;

import com.bobby.springboot.entity.SysPermission;
import com.bobby.springboot.entity.SysRole;
import com.bobby.springboot.entity.UserInfo;
import com.bobby.springboot.service.UserInforService;
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 org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;

/**
 * Created by Bobby on 2019/9/23 14:14
 */
public class MyShiroRealm extends AuthorizingRealm {

    @Autowired
    private UserInforService userService;


// 配置用户权限   访问页面的时  链接配置了相应的权限或者是shiro标签才能执行此方法
//    否则不执行
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("开始配置权限");
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        UserInfo userInfo = (UserInfo)principalCollection.getPrimaryPrincipal();

        for (SysRole role : userInfo.getRoleList()){
//            添加 角色
            info.addRole(role.getRole());
            for (SysPermission p : role.getPermissions()){
//                添加权限
                info.addStringPermission(p.getPermission());
            }
        }
        return info;
    }



//    用户登录身份校验
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("开始身份校验");
        String userName = (String) authenticationToken.getPrincipal();
        UserInfo userInfo = userService.findByUsername(userName);
        if(null == userInfo){
//             //没有返回登录用户名对应的SimpleAuthenticationInfo对象时,
//             就会在LoginController中抛出UnknownAccountException异常
            return null;
        }
//验证通过返回一个封装了用户信息的AuthenticationInfo实例即可。
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
                userInfo,//用户信息
                userInfo.getPassword(),//密码
                getName()//realm name
        );
//        设置盐
        authenticationInfo.setCredentialsSalt(ByteSource.Util.bytes(userInfo.getCredentialsSalt()));
        return authenticationInfo;
    }
}

重载以上两个方法来配置用户身份验证和权限验证。

别忘了在Service包下新建个UserInfoService和它的实现类:

2.3 service方法实现
 

/**
 * Created by Bobby on 2019/9/23 14:18
 */
public interface UserInforService {

    public UserInfo findByUsername(String username);
}
@Service
public class UserInfoServiceImpl implements UserInfoService {

    @Resource
    private UserInfoRepository userInfoRepository;

    @Override
    public UserInfo findByUsername(String username) {

        System.out.println("UserInfoServiceImpl.findByUsername");
        return userInfoRepository.findByUsername(username);
    }
}

2.4 dao

/**
 * Created by Bobby on 2019/9/23 16:27
 */
public interface UserInfoRepository extends CrudRepository<UserInfo, Long> {


 
    public UserInfo findByUsername(String username);

    public UserInfo save(UserInfo userInfo);
}

2.5 接下来配置Shiro的关键部分

这里要配置的是ShiroConfig类,Apache Shiro 核心通过 Filter 来实现,就好像SpringMvc 通过DispachServlet 来主控制一样。 既然是使用 Filter 一般也就能猜到,是通过URL规则来进行过滤和权限校验,所以我们需要定义一系列关于URL的规则和访问权限


/**
 * Created by Bobby on 2019/9/23 14:46
 */
@Configuration
public class ShiroConfiguration {

    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager){
        ShiroFilterFactoryBean shiroFilterFactoryBean =  new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        Map<String,String> filterChainDefinitionMap = new LinkedHashMap();
        filterChainDefinitionMap.put("/logout","logout");
        filterChainDefinitionMap.put("/**","authc");
        filterChainDefinitionMap.put("/favicon.ico","anon");
    //authc 表示需要验证身份证才能访问 ,还有一些比如anon表示不需要验证身份就能访问等。
        shiroFilterFactoryBean.setLoginUrl("/login");
        shiroFilterFactoryBean.setSuccessUrl("/index");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return  shiroFilterFactoryBean;
    }


    //SecurityManager 是 Shiro 架构的核心,通过它来链接Realm和用户(文档中称之为Subject.)
    @Bean
    public SecurityManager securityManager(){
        DefaultWebSecurityManager  securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(myShiroRealm());
        return  securityManager;
    }


    @Bean
    public  MyShiroRealm myShiroRealm(){
        MyShiroRealm myShiroRealm = new MyShiroRealm();
        myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
        return myShiroRealm;
    }

    //因为我们的密码是加过密的,所以,如果要Shiro验证用户身份的话,
    // 需要告诉它我们用的是md5加密的,并且是加密了两次。同时我们在自己的Realm中也通过SimpleAuthenticationInfo返回了加密时使用的盐。
    // 这样Shiro就能顺利的解密密码并验证用户名和密码是否正确了。
    @Bean
    public  HashedCredentialsMatcher hashedCredentialsMatcher(){
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        hashedCredentialsMatcher.setHashAlgorithmName("md5");//散列算法:这里使用MD5算法;
        hashedCredentialsMatcher.setHashIterations(2);//散列的次数,比如散列两次,相当于 md5(md5(""));
        return hashedCredentialsMatcher;
    }

关于为什么设置filterChainDefinitionMap.put("/favicon.ico", "anon");请参考Shiro登录后下载favicon.ico问题

 

2.6 修改我们的HomeController中的/login请求

    @RequestMapping("/login")
    public String login(HttpServletRequest request, Map<String, Object> map) {
        // 登录失败从request中获取shiro处理的异常信息。
        // shiroLoginFailure:就是shiro异常类的全类名.
        Object exception = request.getAttribute("shiroLoginFailure");
        String msg = "";
        if (exception != null) {
            if (UnknownAccountException.class.getName().equals(exception)) {
                System.out.println("账户不存在");
                msg = "账户不存在或密码不正确";
            } else if (IncorrectCredentialsException.class.getName().equals(exception)) {
                System.out.println("密码不正确");
                msg = "账户不存在或密码不正确";
            } else {
                System.out.println("其他异常");
                msg = "其他异常";
            }
        }
        map.put("msg", msg);
        // 此方法不处理登录成功,由shiro进行处理.
        return "login";
    }

这里@RequestMapping之所以没加method是因为如果用户没登录,Shiro会调用get方法请求/login,而后面我们在login页面会用post请求发送form表单,所以这里就没设置method(默认支持所有请求)。

好,启动项目。这时候我们再访问http://localhost:8080/index会跳转到登录页面,因为我们设置了filterChainDefinitionMap.put("/**", "authc");,所以要先验证身份才能访问。同时因为设置了shiroFilterFactoryBean.setLoginUrl("/login");,所以会跳转到登录页面。这时候在登录页面输入正确的用户名密码就可以登录了,登录成功会自动跳转到index页面。

 

(三)权限 (精确到按钮级别的权限控制)

3.1在以上的基础上,添加一个UserInfoController

package com.bobby.springboot.controller;

import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * Created by Bobby on 2019/9/23 17:00
 */
@Controller
@RequestMapping("/userinfor")
public class UserInfoController {


    /**
     * 用户查询.
     * @return
     */
    @RequestMapping("/userList")
    @RequiresPermissions("userInfo:view")
    public String userInfo(){
        return "userInfo";
    }

    /**
     * 用户添加;
     * @return
     */
    @RequestMapping("/userAdd")
    @RequiresPermissions("userInfo:add")
    public String userInfoAdd(){
        return "userAdd";
    }

    @RequestMapping("/userDel")
    @RequiresPermissions("userInfo:del")
    public String userInfoDel() {
        return "userDel";
    }
}

这时候启动项目,上面的几个请求在登录之后我们都是可以访问的,并且不会执行doGetAuthorizationInfo()打印权限验证信息。如果我们要限制某些人必须有一定的权限才能访问,怎么办呢?

3.2 开启Shiro AOP注解支持

ShiroConfiguration中加入以下代码开启:

  /**
     *  开启shiro aop注解支持.
     *  使用代理方式;所以需要开启代码支持;
     * @param securityManager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

3.3然后,在controller中加入相应的注解即可:

 /**
     * 用户查询.
     * @return
     */
    @RequestMapping("/userList")
    @RequiresPermissions("userInfo:view")
    public String userInfo(){
        return "userInfo";
    }

    /**
     * 用户添加;
     * @return
     */
    @RequestMapping("/userAdd")
    @RequiresPermissions("userInfo:add")
    public String userInfoAdd(){
        return "userAdd";
    }

    @RequestMapping("/userDel")
    @RequiresPermissions("userInfo:del")
    public String userInfoDel() {
        return "userDel";
    }

这时候,我们再登录之后访问http://localhost:8080/userInfo/userDel就会报org.apache.shiro.authz.AuthorizationException异常了。同时后台会打印权限验证的信息。

关于怎么处理这个没权限的异常,如果是在ShiroConfiguration中配置403是不起作用的,具体请参考setUnauthorizedUrl("/403")不起作用.

说下,按钮级别的权限控制,在我们的项目中,我们使用的Beetl这个前段模板+layui.js(吐槽下,真的不是很好用喃,做数据渲染的时候),在显示的过程中,会在模板中判断是否有权限,如同@RequiresPermissions("userInfo:add")这个教研的效果是一样的,通过访问用户是否有该权限字段,来判断按钮牛是否显示,这样做到按钮级别的权限控制。

 

上面所有的代码参考 : 工程参考 springboot整合shiro 完整版 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值