NASI

SpringBoot中关于Shiro权限管理

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

一、 shiro是一个轻量级的安全框架,包含用户认证和用户授权

分析shiro的核心API:
Subject:用户主体(把操作交给SecurityManager)
SecurityManager:安全管理器(管理Reaml)
Reaml:shiro连接数据的桥梁

Shiro的配置类:
创建ShiroFilterFactoryBean;
创建DefaultWebSecurityManager;
创建Reaml(继承AuthorizingReaml);

Shiro内置过滤器,可以实现权限相关的拦截器:
常用的过滤器:
anno:无需认证(登陆)可以访问
authc:必须认证才能访问
user:如果使用rememberMe的功能可以直接访问
perms:该资源必须得到资源权限可以访问
role:该资源必须得到角色权限才能访问

在整合Shiro的时候,我们先要确定一下我们的步骤:
1.加入Shiro的依赖包,实现自己的Realm类(通过继承AuthorizingRealm类);
2.实现Shiro的配置类
3.实现前端的登录界面以及Controller类

第一步:
在pom.xml中加入依赖包

org.apache.shiro shiro-spring 1.4.0

实现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 roles = new HashSet();
      roles.add(“role1”);
      authorizationInfo.setRoles(roles);
      //权限
      Set permissions = new HashSet();
      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”);
    //
    /初始化所有的权限信息开始
    ***/
    //这里,如果以后再项目中使用的话,直接从数据库中查询
    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”%>

Login

登录页面----${error}

用户名:
密码:

2.index.jsp

<%@ page language=“java” pageEncoding=“UTF-8”%>
<%@taglib prefix=“shiro” uri=“http://shiro.apache.org/tags” %>

第一个例子

${name}:你好,欢迎访问该网页

跳转到用户列表(有权限)
跳转到新增用户列表(无权限)

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”%>

userList

用户列表信息

上面就是全部代码了,如果启动成功,进入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”);
      //
      /初始化所有的权限信息开始
      ***/
      //这里,如果以后再项目中使用的话,直接从数据库中查询
      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权限出现错误!”);
      }
      }
      }

}

下面是mysql库的表结构

/*
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=‘角色资源权限表中间表’;

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值