SpringBoot中关于Shiro权限管理的整合使用

举报
tea_year 发表于 2021/12/22 23:34:24 2021/12/22
2.3k+ 0 0
【摘要】 在整合Shiro的时候,我们先要确定一下我们的步骤: 1.加入Shiro的依赖包,实现自己的Realm类(通过继承AuthorizingRealm类); 2.实现Shiro的配置类 3.实现前端的登录界面以及Controller类   第一步: 在pom.xml中加入依赖包 <dependency> ...

在整合Shiro的时候,我们先要确定一下我们的步骤:

1.加入Shiro的依赖包,实现自己的Realm类(通过继承AuthorizingRealm类);

2.实现Shiro的配置类

3.实现前端的登录界面以及Controller类

第一步:

在pom.xml中加入依赖包


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

实现Realm类


      package ariky.shiro.realm;
      import java.util.HashSet;
      import java.util.Set;
      import javax.servlet.http.HttpServletRequest;
      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.IncorrectCredentialsException;
      import org.apache.shiro.authc.LockedAccountException;
      import org.apache.shiro.authc.SimpleAuthenticationInfo;
      import org.apache.shiro.authc.UsernamePasswordToken;
      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.apache.shiro.web.subject.WebSubject;
      import org.slf4j.Logger;
      import org.slf4j.LoggerFactory;
      /**
      * @ClassName:
      * @Description: Realm的配置
      * @author fuweilian
      * @date 2018-5-12 上午11:36:41
       */
      public class MyShiroRealm extends AuthorizingRealm {
         //slf4j记录日志,可以不使用
         private Logger logger = LoggerFactory.getLogger(MyShiroRealm.class);
         /**
       * 设置授权信息
       */
         @Override
         protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
              logger.info("开始授权(doGetAuthorizationInfo)");
             SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
             HttpServletRequest request = (HttpServletRequest) ((WebSubject) SecurityUtils
                      .getSubject()).getServletRequest();//这个可以用来获取在登录的时候提交的其他额外的参数信息
             String username = (String) principals.getPrimaryPrincipal();//这里是写的demo,后面在实际项目中药通过这个登录的账号去获取用户的角色和权限,这里直接是写死的
             //受理权限
             //角色
              Set<String> roles = new HashSet<String>();
              roles.add("role1");
              authorizationInfo.setRoles(roles);
             //权限
              Set<String> permissions = new HashSet<String>();
              permissions.add("user:list");
             //permissions.add("user:add");
              authorizationInfo.setStringPermissions(permissions);
             return authorizationInfo;
          }
         /**
       * 设置认证信息
       */
         @Override
         protected AuthenticationInfo doGetAuthenticationInfo(
       AuthenticationToken authenticationToken) throws AuthenticationException {
              logger.info("开始认证(doGetAuthenticationInfo)");
             //UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
             HttpServletRequest request = (HttpServletRequest) ((WebSubject) SecurityUtils
                      .getSubject()).getServletRequest();
             UsernamePasswordToken token = new UsernamePasswordToken (request.getParameter("userName"),request.getParameter("password"));
             //获取用户输入的账号
             String userName = (String)token.getPrincipal();
             //通过userName去数据库中匹配用户信息,通过查询用户的情况做下面的处理
             //这里暂时就直接写死,根据登录用户账号的情况做处理
              logger.info("账号:"+userName);
             if("passwordError".equals(userName)){//密码错误
                 throw new IncorrectCredentialsException();
              }else if("lockAccount".equals(userName)){// 用户锁定
                 throw new LockedAccountException();
              }else{
                 SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
                          userName, //用户名
                         "123456", //密码,写死
                          ByteSource.Util.bytes(userName+"salt"),//salt=username+salt
                          getName()  //realm name
                  );
                 return authenticationInfo;
              }
          }
      }
  
 

第二步 实现Shiro的配置类:


      package ariky.shiro.configuration;
      import java.util.LinkedHashMap;
      import java.util.Map;
      import org.apache.shiro.mgt.SecurityManager;
      import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
      import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
      import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
      import org.slf4j.Logger;
      import org.slf4j.LoggerFactory;
      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.Configuration;
      import ariky.shiro.realm.MyShiroRealm;
      /**
      * @ClassName: ShiroConfiguration
      * @Description: shiro的配置类
      * @author fuweilian
      * @date 2018-5-12 上午11:05:09
       */
      @Configuration
      public class ShiroConfiguration {
         private static Logger logger = LoggerFactory.getLogger(ShiroConfiguration.class);
         @Bean(name = "shiroFilter")
         public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager){
              logger.info("进入shiroFilter......");
             ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
              shiroFilterFactoryBean.setSecurityManager(securityManager);
             //设置不需要拦截的路径
              Map<String,String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
             //按顺序依次判断
              filterChainDefinitionMap.put("/static/**", "anon");
             //配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了
              filterChainDefinitionMap.put("/logout", "logout");
             //<!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->
             /************************************初始化所有的权限信息开始******************************************/
             //这里,如果以后再项目中使用的话,直接从数据库中查询
              filterChainDefinitionMap.put("/user/list", "authc,perms[user:list]");
             //filterChainDefinitionMap.put("/user/add", "authc,perms[user:add]");
             /***************************************初始化所有的权限信息开始结束*********************************************/
              filterChainDefinitionMap.put("/**", "authc");
             // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
              shiroFilterFactoryBean.setLoginUrl("/login");
             // 登录成功后要跳转的链接
              shiroFilterFactoryBean.setSuccessUrl("/index");
             //未授权界面
              shiroFilterFactoryBean.setUnauthorizedUrl("/error/403");
              shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
             return shiroFilterFactoryBean;
          }
         @Bean
         public MyShiroRealm myShiroRealm(){
              MyShiroRealm myShiroRealm = new MyShiroRealm();
              //后面这里可以设置缓存的机制
              return myShiroRealm;
          }
         @Bean
         public SecurityManager securityManager(){
             DefaultWebSecurityManager securityManager =  new DefaultWebSecurityManager();
              securityManager.setRealm(myShiroRealm());
             return securityManager;
          }
         @Bean
         public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
             AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
              authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
             return authorizationAttributeSourceAdvisor;
          }
      }
  
 

第三步:实现Controoler类,这里写俩个类,一个是登录信息的LoginController处理类,一个是测试权限用的UserController

1.LoginController.java


      package ariky.controller;
      import java.util.Map;
      import javax.servlet.http.HttpServletRequest;
      import org.apache.shiro.SecurityUtils;
      import org.apache.shiro.authc.IncorrectCredentialsException;
      import org.apache.shiro.authc.LockedAccountException;
      import org.apache.shiro.authc.UnknownAccountException;
      import org.slf4j.Logger;
      import org.slf4j.LoggerFactory;
      import org.springframework.stereotype.Controller;
      import org.springframework.web.bind.annotation.RequestMapping;
      import org.springframework.web.bind.annotation.RequestMethod;
      /**
      * @ClassName: LoginController
      * @Description: 登录控制的controller
      * @author fuweilian
      * @date 2018-5-12 下午01:15:46
       */
      @RequestMapping
      @Controller
      public class LoginController {
         private Logger logger = LoggerFactory.getLogger(LoginController.class);
         @RequestMapping(value="/login",method=RequestMethod.GET)
         public String getLogin(){
              logger.info("进入login页面");
             return "login";
          }
         @RequestMapping(value="/login",method=RequestMethod.POST)
         public String doLogin(HttpServletRequest req,Map<String, Object> model){
              logger.info("进入登录处理");
             String exceptionClassName = (String) req.getAttribute("shiroLoginFailure");
              logger.info("exceptionClassName:"+exceptionClassName);
             String error = null;
             if (UnknownAccountException.class.getName().equals(exceptionClassName)) {
                  error = "用户名/密码错误";
              } else if (IncorrectCredentialsException.class.getName().equals(exceptionClassName)) {
                  error = "用户名/密码错误";
              }else if(LockedAccountException.class.getName().equals(exceptionClassName)){
                  error = "用户已锁定或已删除";
              }else if (exceptionClassName != null) {
                  error = "其他错误:" + exceptionClassName;
              }
             if(SecurityUtils.getSubject().isAuthenticated()){//没有错误,但是已经登录了,就直接跳转到welcom页面
                  model.put("name", req.getParameter("userName"));
                 return "index";
              }else{//有错误的
                  model.put("error", error);
                 return "login";
              }
          }
         @RequestMapping("/index")
         public String index(){
             return "index";
          }
      }
  
 

2.UserController.java


      package ariky.controller;
      import java.util.ArrayList;
      import java.util.List;
      import org.apache.shiro.authz.annotation.RequiresPermissions;
      import org.slf4j.Logger;
      import org.slf4j.LoggerFactory;
      import org.springframework.stereotype.Controller;
      import org.springframework.web.bind.annotation.RequestMapping;
      import org.springframework.web.bind.annotation.RequestMethod;
      import org.springframework.web.bind.annotation.ResponseBody;
      /**
      * @ClassName: UserController
      * @Description: 用户处理Controller
      * @author fuweilian
      * @date 2018-5-12 下午03:11:06
       */
      @Controller
      @RequestMapping("/user")
      public class UserController {
         Logger logger = LoggerFactory.getLogger(UserController.class);
         @RequiresPermissions("user:list")//这个是配置是否有该权限的,如果是按上面的写法,这个是有权限的
         @RequestMapping(value="/list",method=RequestMethod.GET)
         public String getList(){
              logger.info("进入用户列表");
             return "user/list";
          }
         @RequiresPermissions(value={"user:add"})//这个是没有权限的
         @RequestMapping(value="/add",method=RequestMethod.GET)
         public String getAdd(){
              logger.info("进入新增用户界面");
             return "user/add";
          }
      }
  
 

前端界面:有5个界面 (login.jsp,index.jsp,list.jsp,add.jsp,403.jsp)

目录结构为:

1.login.jsp


      <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
      <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
      <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
      <html>
      <head>
      <title>Login</title>
      </head>
      <body>
         <h1>登录页面----${error}</h1>
         <form:form action="${pageContext.request.contextPath }/login"
              method="post">
              用户名:<input type="text" name="userName">
             <br />
              密码:<input type="passwordParam" name="password"/>
             <input type="submit" value="提交"/>
         </form:form>
      </body>
      </html>
  
 

2.index.jsp


      <%@ page language="java" pageEncoding="UTF-8"%>
       <%@taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
      <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
      <html>
      <head>
      <title>第一个例子</title>
      <script src="${pageContext.request.contextPath }/webjars/jquery/2.1.4/jquery.js"></script>
      <script src="${pageContext.request.contextPath }/webjarslocator/jquery/jquery.js"></script>
      </head>
      <body>
         <h1>${name}:你好,欢迎访问该网页</h1>
             <shiro:hasPermission name="user:list"><!-- 这个a标签是可以看见的 -->
                 <a href="${pageContext.request.contextPath }/user/list" target="_blank">跳转到用户列表(有权限)</a>
             </shiro:hasPermission>
         <br/>
             <shiro:hasPermission name="user:add"><!-- 这个a标签是看不见的 -->
                 <a href="${pageContext.request.contextPath }/user/add" target="_blank">跳转到新增用户列表(无权限)</a>
             </shiro:hasPermission>
      </body>
      </html>
  
 

3.list.jsp和add.jsp以及403.jsp都差不多一样,这里就写一个,这里只是demo所用,在实际项目中,要以实际项目为准


      <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
      <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
      <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
      <html>
      <head>
      <title>userList</title>
      </head>
      <body>
         <h1>用户列表信息</h1>
      </body>
      </html>
  
 

如果启动成功,进入login登录界面就可以测试一下shiro的权限认证了。上面的代码都是写死的,如果想要实现动态的权限管理和用户的权限管理的话,还要做一些其他处理,用户的动态权限这个只要在自己的ShiroRealm类里面授权的时候做一下查询数据库,动态的授权和角色就行。关于动态的权限管理的话,下面的方式可以实现,在修改完权限数据后,更新一下shiro里面的配置就行,具体看下面的代码,这里是demo,不是实际项目,在实际项目中最好不要把逻辑写在Controller里面


      package ariky.shiro.controller;
      import java.util.LinkedHashMap;
      import java.util.Map;
      import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
      import org.apache.shiro.web.filter.mgt.DefaultFilterChainManager;
      import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver;
      import org.apache.shiro.web.servlet.AbstractShiroFilter;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.stereotype.Controller;
      import org.springframework.web.bind.annotation.RequestMapping;
      import org.springframework.web.bind.annotation.ResponseBody;
      /**
      * @ClassName: PermssionController
      * @Description: 权限操作的controller
      * @author fuweilian
      * @date 2018-5-12 下午04:59:15
       */
      @Controller
      @RequestMapping("permssion")
      public class PermssionController {
          @Autowired
           ShiroFilterFactoryBean shiroFilterFactoryBean;
          /**
       * @Title: updatePermssion
       * @author: fuweilian
       * @Description: 这里暂时直接写在controller里面,,不按规则写了,,到时候在项目中使用的时候,才写
       * @return 参数说明
       * @return Object 返回类型
       * @throws
       */
         @RequestMapping("/updatePermssion")
         @ResponseBody
         public Object updatePermssion(){
             synchronized (shiroFilterFactoryBean){
                 AbstractShiroFilter shiroFilter = null;
                 try {
                      shiroFilter = (AbstractShiroFilter) shiroFilterFactoryBean
                              .getObject();
                     PathMatchingFilterChainResolver filterChainResolver = (PathMatchingFilterChainResolver) shiroFilter
                          .getFilterChainResolver();
                     DefaultFilterChainManager manager = (DefaultFilterChainManager) filterChainResolver
                          .getFilterChainManager();
                     // 清空老的权限控制
                      manager.getFilterChains().clear();
                      shiroFilterFactoryBean.getFilterChainDefinitionMap().clear();
                     //后面这个可以直接从数据库里面获取
                      Map<String,String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
                     //按顺序依次判断
                      filterChainDefinitionMap.put("/static/**", "anon");
                     //配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了
                      filterChainDefinitionMap.put("/logout", "logout");
                     //<!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->
                     /************************************初始化所有的权限信息开始******************************************/
                     //这里,如果以后再项目中使用的话,直接从数据库中查询
                      filterChainDefinitionMap.put("/user/list", "authc,perms[user:list]");
                      filterChainDefinitionMap.put("/user/add", "authc,perms[user:add]");
                     /***************************************初始化所有的权限信息开始结束*********************************************/
                      filterChainDefinitionMap.put("/**", "authc");
                     //
                      shiroFilterFactoryBean.setLoginUrl("/login");
                     // 登录成功后要跳转的链接
                      shiroFilterFactoryBean.setSuccessUrl("/index");
                     //未授权界面
                      shiroFilterFactoryBean.setUnauthorizedUrl("/error/403");
                      shiroFilterFactoryBean
                          .setFilterChainDefinitionMap(filterChainDefinitionMap);
                     // 重新构建生成
                      Map<String, String> chains = shiroFilterFactoryBean
                              .getFilterChainDefinitionMap();
                     for (Map.Entry<String, String> entry : chains.entrySet()) {
                         String url = entry.getKey();
                         String chainDefinition = entry.getValue().trim()
                                  .replace(" ", "");
                          manager.createChain(url, chainDefinition);
                      }
                     return "更新权限成功";
                  } catch (Exception e) {
                     throw new RuntimeException(
                             "更新shiro权限出现错误!");
                  }
              }
          }
      }
  
 

      /*
      Navicat MySQL Data Transfer
      Source Server : arikyDB
      Source Server Version : 50721
      Source Host : 47.106.95.168:3306
      Source Database : ariky
      Target Server Type : MYSQL
      Target Server Version : 50721
      File Encoding : 65001
      Date: 2018-05-14 16:05:51
      */
      SET FOREIGN_KEY_CHECKS=0;
      -- ----------------------------
      -- Table structure for common_permssion
      -- ----------------------------
      DROP TABLE IF EXISTS `common_permssion`;
      CREATE TABLE `common_permssion` (
        `ID` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
        `NAME` varchar(255) DEFAULT NULL COMMENT '权限名称',
        `TYPE` varchar(255) DEFAULT NULL COMMENT '类型按钮(button)或者菜单(menu) ',
        `PARENT_ID` int(11) DEFAULT NULL COMMENT '上级ID',
        `PARENT_IDS` varchar(255) DEFAULT NULL COMMENT '上级PIDs',
        `URL` varchar(255) DEFAULT NULL COMMENT '访问路径',
        `ICONCLS` varchar(255) DEFAULT NULL COMMENT '图标(可以不要)',
        `PERMISSION` varchar(255) DEFAULT NULL COMMENT '权限(如user:list)',
        `ORDER_NUM` int(11) DEFAULT NULL COMMENT '排序',
        `REMARK` varchar(255) DEFAULT NULL COMMENT '备注',
       PRIMARY KEY (`ID`)
      ) ENGINE=InnoDB AUTO_INCREMENT=41 DEFAULT CHARSET=utf8 COMMENT='该表用来存储资源权限信息';
      -- ----------------------------
      -- Table structure for common_role
      -- ----------------------------
      DROP TABLE IF EXISTS `common_role`;
      CREATE TABLE `common_role` (
        `ID` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
        `LABEL_ID` varchar(255) DEFAULT NULL COMMENT '标签Id',
        `NAME` varchar(255) DEFAULT NULL COMMENT '角色名称',
        `ROLE` varchar(255) DEFAULT NULL,
        `DESCRIPTION` varchar(255) DEFAULT NULL,
        `IS_SHOW` int(11) DEFAULT '1' COMMENT '判断该角色是否在使用(1:使用,2:禁用)',
        `IS_HANDLER` int(2) DEFAULT NULL COMMENT '判断是什么角色(1:后台角色,2:商家管理员角色,3:商家添加用户角色,4:游客角色)',
       PRIMARY KEY (`ID`)
      ) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8 COMMENT='角色表';
      -- ----------------------------
      -- Table structure for common_role_permssion
      -- ----------------------------
      DROP TABLE IF EXISTS `common_role_permssion`;
      CREATE TABLE `common_role_permssion` (
        `ID` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键Id',
        `ROLE_ID` int(11) DEFAULT NULL COMMENT '角色Id',
        `RESOURCE_ID` int(11) DEFAULT NULL COMMENT '资源(权限)Id',
       PRIMARY KEY (`ID`)
      ) ENGINE=InnoDB AUTO_INCREMENT=493 DEFAULT CHARSET=utf8 COMMENT='角色资源权限表中间表';
  
 

文章来源: aaaedu.blog.csdn.net,作者:tea_year,版权归原作者所有,如需转载,请联系作者。

原文链接:aaaedu.blog.csdn.net/article/details/104625839

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

作者其他文章

评论(0

抱歉,系统识别当前为高风险访问,暂不支持该操作

    全部回复

    上滑加载中

    设置昵称

    在此一键设置昵称,即可参与社区互动!

    *长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

    *长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。