Spring +mybatisplus+shiro权限管理集成整合


 

一、Apache Shiro是一个功能强大、灵活的,开源的安全框架。它可以干净利落地处理身份验证、授权、企业会话管理和加密。

 

Shiro能做什么呢?

验证用户身份

用户访问权限控制,比如:1、判断用户是否分配了一定的安全角色。2、判断用户是否被授予完成某个操作的权限

在非 web 或 EJB 容器的环境下可以任意使用Session API

可以响应认证、访问控制,或者Session 生命周期中发生的事件

可将一个或以上用户安全数据源数据组合成一个复合的用户 “view”(视图)

支持单点登录(SSO)功能

支持提供“Remember Me”服务,获取用户关联信息而无需登录 

 

 

Shiro 致力在所有应用环境下实现上述功能,小到命令行应用程序,大到企业应用中,而且不需要借助第三方框架、容器、应用服务器等。当然 Shiro 的目的是尽量的融入到这样的应用环境中去,但也可以在它们之外的任何环境下开箱即用。

Mybatis-Plus(简称MP)是一个 Mybatis 的增强工具,在 Mybatis 的基础上只做增强不做改变,简化开发、提高效率。

二、

集成Shiro我们需要知道Shiro框架大概的一些管理对象。

     1、ShiroFilterFactory:Shiro过滤工厂类,具体的实现类是ShiroFilterFactoryBean,该实现类依赖与SecurityManage安全管理器。

   2、SecurityManage:Shiro的安全管理,主要是安全认证管理、缓存管理、cookie管理。所以在实际的开发中我们主要和SecurityManage打交道,ShiroFilterFactory主要配置好了Filter就可以了。

   3、Reamls:身份信息、权限信息的验证。

   4、缓存管理、记住密码等功能,这些大部分只要进行简单的实现,然后注入到SecurityManage让Shiro的安全管理器进行管理就好了。

5、Authentication用于验证用户身份

6、Authorization用于授权访问控制。

7、Shiro核心是通过Filter来实现,就好像SpringMVC用DispatchServlet来做控制一样。既然使用Filter,那么我们也可以猜到,Shiro是通过URL规则来进行过滤和权限校验,所以我们需要定义一系列的URL规则和访问权限。

三、项目结构

Spring +mybatisplus+shiro权限管理集成整合_第1张图片

四、pom.xml

 

xml version="1.0" encoding="UTF-8"?>
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.0http://maven.apache.org/xsd/maven-4.0.0.xsd">
  
4.0.0

  
com.gaox
  
shiroDemo
  
0.0.1-SNAPSHOT
  
jar

  
shiroDemo
  
Demo project for Spring Boot

  
     
org.springframework.boot
     
spring-boot-starter-parent
     
2.0.1.RELEASE
     

  


  
     
UTF-8
     
UTF-8
     
1.8
  

  
     
        
org.springframework.boot
        
spring-boot-starter-web
     

     
        
org.springframework.boot
        
spring-boot-starter-test
        
test
     
     
     
        
org.springframework.boot
        
spring-boot-starter-thymeleaf
     
     
        
net.sourceforge.nekohtml
        
nekohtml
        
1.9.22
     
     

      

        
org.apache.shiro
        
shiro-spring
        
1.4.0
     
     
        
org.apache.shiro
        
shiro-ehcache
        
1.2.5
     
     

     

        
mysql
        
mysql-connector-java
        
runtime
     
     

     

        
com.baomidou
        
mybatis-plus
        
2.2.0
     
     
        
org.mybatis.spring.boot
        
mybatis-spring-boot-starter
        
1.3.2
     

  


  
     
shiroDemo

     
        
           
org.springframework.boot
           
spring-boot-maven-plugin
        
     

  


 

五、application.properties

spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

spring.thymeleaf.cache=false
spring.thymeleaf.mode=LEGACYHTML5

server.port=8080
spring.application.name=shiroDemo

#tomcat
server.max-http-header-size=1048576
server.tomcat.max-connections=3000
server.tomcat.max-http-post-size=1048576
server.tomcat.max-threads=1000

mybatis.type-aliases-package=com.gaox.entity
mybatis.mapper-locations=classpath:/mybatis/mapper/*Mapper.xml
mybatis.config-location=classpath:/mybatis/mybatis_config.xml

六、缓存文件配置ehcache.xml

xml version="1.0" encoding="UTF-8"?>
name="shirocache">
    path="java.io.tmpdir"/>
    maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            overflowToDisk="false"
            diskPersistent="false"
            diskExpiryThreadIntervalSeconds="120"
    />
    
    name="authorizationCache"
           maxEntriesLocalHeap="2000"
           eternal="false"
           timeToIdleSeconds="3600"
           timeToLiveSeconds="0"
           overflowToDisk="false"
           statistics="true">
    
    
    name="authenticationCache"
           maxEntriesLocalHeap="2000"
           eternal="false"
           timeToIdleSeconds="3600"
           timeToLiveSeconds="0"
           overflowToDisk="false"
           statistics="true">
    

七、定义自己的Realm :MyShiroRealm.java

 

package com.gaox.config;


import com.gaox.entity.UserInfo;
import com.gaox.service.UserInfoService;
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 javax.annotation.Resource;
import java.util.Set;

public class MyShiroRealm extends AuthorizingRealm {
    @Resource
    private UserInfoService userInfoService;

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        System.out.println("权限配置-->MyShiroRealm.doGetAuthorizationInfo()");
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        UserInfo userInfo  = (UserInfo)principals.getPrimaryPrincipal();
        Set roles=userInfoService.findRoleByUser(userInfo.getUsername());
        Set permissions=userInfoService.findPermissionByUser(userInfo.getUsername());
        authorizationInfo.setRoles(roles);
        authorizationInfo.setStringPermissions(permissions);
        return authorizationInfo;
    }

    /*主要是用来进行身份认证的,也就是说验证用户输入的账号和密码是否正确。*/
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)
            throws AuthenticationException {
        System.out.println("MyShiroRealm.doGetAuthenticationInfo()");
        //获取用户的输入的账号.
        String username = (String)token.getPrincipal();
        System.out.println(token.getCredentials());
        //通过username从数据库中查找 User对象,如果找到,没找到.
        //实际项目中,这里可以根据实际情况做缓存,如果不做,Shiro自己也是有时间间隔机制,2分钟内不会重复执行该方法
        UserInfo userInfo = userInfoService.findByUsername(username);
        System.out.println("----->>userInfo="+userInfo);
        if(userInfo == null){
            return null;
        }
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
                userInfo, //用户名
                userInfo.getPassword(), //密码
                ByteSource.Util.bytes(userInfo.getCredentialsSalt()),//salt=username+salt
                getName()  //realm name
        );
        return authenticationInfo;
    }

}

八、配置ShiroConfig.java

package com.gaox.config;

import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.ehcache.EhCacheManager;
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.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;

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

@Configuration
public class ShiroConfig {

   @Bean(name = "lifecycleBeanPostProcessor")
   public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
      return new LifecycleBeanPostProcessor();
   }

   @Bean
   @ConditionalOnMissingBean
   public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
      DefaultAdvisorAutoProxyCreator daap = new DefaultAdvisorAutoProxyCreator();
      daap.setProxyTargetClass(true);
      return daap;
   }
   @Bean
   public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
      System.out.println("ShiroConfiguration.shirFilter()");
      ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
      shiroFilterFactoryBean.setSecurityManager(securityManager);
      //拦截器.
      Map,String> filterChainDefinitionMap = new LinkedHashMap,String>();
      // 配置不会被拦截的链接 顺序判断
      filterChainDefinitionMap.put("/static/**", "anon");
      //配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了
      filterChainDefinitionMap.put("/logout", "logout");
      //:这是一个坑呢,一不小心代码就不好使了;
      //
      filterChainDefinitionMap.put("/**", "authc");
      // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
      shiroFilterFactoryBean.setLoginUrl("/login");
      // 登录成功后要跳转的链接
      shiroFilterFactoryBean.setSuccessUrl("/index");

      //未授权界面;
      shiroFilterFactoryBean.setUnauthorizedUrl("/403");
      shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
      return shiroFilterFactoryBean;
   }

   /**
    * 凭证匹配器
    * (由于我们的密码校验交给ShiroSimpleAuthenticationInfo进行处理了
    * )
    * @return
    */
   @Bean
   public HashedCredentialsMatcher hashedCredentialsMatcher(){
      HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
      hashedCredentialsMatcher.setHashAlgorithmName("md5");//散列算法:这里使用MD5算法;
      hashedCredentialsMatcher.setHashIterations(2);//散列的次数,比如散列两次,相当于 md5(md5(""));
      return hashedCredentialsMatcher;
   }

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

   @Bean(name = "ehCacheManager")
   @DependsOn("lifecycleBeanPostProcessor")
   public EhCacheManager ehCacheManager(){
      EhCacheManager ehCacheManager = new EhCacheManager();
      ehCacheManager.setCacheManagerConfigFile("classpath:ehcache.xml");
      return ehCacheManager;
   }
   @Bean
   public SecurityManager securityManager(){
      DefaultWebSecurityManager securityManager =  new DefaultWebSecurityManager();
      securityManager.setRealm(myShiroRealm());
      securityManager.setCacheManager(ehCacheManager());
      return securityManager;
   }

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

   @Bean(name="simpleMappingExceptionResolver")
   public SimpleMappingExceptionResolver
   createSimpleMappingExceptionResolver() {
      SimpleMappingExceptionResolver r = new SimpleMappingExceptionResolver();
      Properties mappings = new Properties();
      mappings.setProperty("DatabaseException", "databaseError");//数据库异常处理
      mappings.setProperty("UnauthorizedException","403");
      r.setExceptionMappings(mappings)// None by default
      r.setDefaultErrorView("error");    // No default
      r.setExceptionAttribute("ex");     // Default is "exception"
      //r.setWarnLogCategory("example.MvcLogger");     // No default
      return r;
   }
}

九、sql脚本

/*

Navicat MySQL Data Transfer

 

Source Server         : 127.0.0.1

Source Server Version : 50552

Source Host           : 127.0.0.1:3306

Source Database       : test

 

Target Server Type    : MYSQL

Target Server Version : 50552

File Encoding         : 65001

 

Date: 2018-05-03 14:08:09

*/

 

SET FOREIGN_KEY_CHECKS=0;

 

-- ----------------------------

-- Table structure for hibernate_sequence

-- ----------------------------

DROP TABLE IF EXISTS `hibernate_sequence`;

CREATE TABLE `hibernate_sequence` (

 `next_val` bigint(20) DEFAULT NULL

) ENGINE=MyISAM DEFAULT CHARSET=latin1;

 

-- ----------------------------

-- Records of hibernate_sequence

-- ----------------------------

 

-- ----------------------------

-- Table structure for sys_permission

-- ----------------------------

DROP TABLE IF EXISTS `sys_permission`;

CREATE TABLE `sys_permission` (

 `id` int(11) NOT NULL,

 `available` bit(1) DEFAULT NULL,

 `name` varchar(255) CHARACTER SET utf8 DEFAULT NULL,

 `parent_id` bigint(20) DEFAULT NULL,

 `parent_ids` varchar(255) CHARACTER SET utf8 DEFAULT NULL,

 `permission` varchar(255) CHARACTER SET utf8 DEFAULT NULL,

 `resource_type` enum('menu','button') DEFAULT NULL,

 `url` varchar(255) CHARACTER SET utf8 DEFAULT NULL,

 PRIMARY KEY (`id`)

) ENGINE=MyISAM DEFAULT CHARSET=latin1;

 

-- ----------------------------

-- Records of sys_permission

-- ----------------------------

INSERT INTO `sys_permission` VALUES ('1','\0', '用户管理', '0', '0/', 'userInfo:view', 'menu','userInfo/userList');

INSERT INTO `sys_permission` VALUES ('2','\0', '用户添加', '1', '0/1', 'userInfo:add', 'button','userInfo/userAdd');

INSERT INTO `sys_permission` VALUES ('3','\0', '用户删除', '1', '0/1', 'userInfo:del', 'button','userInfo/userDel');

 

-- ----------------------------

-- Table structure for sys_role

-- ----------------------------

DROP TABLE IF EXISTS `sys_role`;

CREATE TABLE `sys_role` (

 `id` int(11) NOT NULL AUTO_INCREMENT,

 `available` bit(1) DEFAULT NULL,

 `description` varchar(255) CHARACTER SET utf8 DEFAULT NULL,

 `role` varchar(255) CHARACTER SET utf8 DEFAULT NULL,

 PRIMARY KEY (`id`)

) ENGINE=MyISAM AUTO_INCREMENT=4 DEFAULTCHARSET=latin1;

 

-- ----------------------------

-- Records of sys_role

-- ----------------------------

INSERT INTO `sys_role` VALUES ('1', '\0', '管理员', 'admin');

INSERT INTO `sys_role` VALUES ('2', '\0','VIP会员', 'vip');

INSERT INTO `sys_role` VALUES ('3', '','test', 'test');

 

-- ----------------------------

-- Table structure for sys_role_permission

-- ----------------------------

DROP TABLE IF EXISTS `sys_role_permission`;

CREATE TABLE `sys_role_permission` (

 `permission_id` int(11) NOT NULL,

 `role_id` int(11) NOT NULL,

  KEY`FK9q28ewrhntqeipl1t04kh1be7` (`role_id`),

  KEY`FKomxrs8a388bknvhjokh440waq` (`permission_id`)

) ENGINE=MyISAM DEFAULT CHARSET=latin1;

 

-- ----------------------------

-- Records of sys_role_permission

-- ----------------------------

INSERT INTO `sys_role_permission` VALUES('2', '1');

INSERT INTO `sys_role_permission` VALUES('3', '2');

 

-- ----------------------------

-- Table structure for sys_user_role

-- ----------------------------

DROP TABLE IF EXISTS `sys_user_role`;

CREATE TABLE `sys_user_role` (

 `uid` int(11) NOT NULL,

 `role_id` int(11) NOT NULL,

  KEY`FKhh52n8vd4ny9ff4x9fb8v65qx` (`role_id`),

  KEY`FKgkmyslkrfeyn9ukmolvek8b8f` (`uid`)

) ENGINE=MyISAM DEFAULT CHARSET=latin1;

 

-- ----------------------------

-- Records of sys_user_role

-- ----------------------------

INSERT INTO `sys_user_role` VALUES ('1','1');

 

-- ----------------------------

-- Table structure for user_info

-- ----------------------------

DROP TABLE IF EXISTS `user_info`;

CREATE TABLE `user_info` (

 `uid` int(11) NOT NULL,

 `name` varchar(255) CHARACTER SET utf8 DEFAULT NULL,

 `password` varchar(255) CHARACTER SET utf8 DEFAULT NULL,

 `salt` varchar(255) CHARACTER SET utf8 DEFAULT NULL,

 `state` tinyint(4) NOT NULL,

 `username` varchar(255) CHARACTER SET utf8 DEFAULT NULL,

 PRIMARY KEY (`uid`),

 UNIQUE KEY `UK_f2ksd6h8hsjtd57ipfq9myr64` (`username`)

) ENGINE=MyISAM DEFAULT CHARSET=latin1;

 

-- ----------------------------

-- Records of user_info

-- ----------------------------

INSERT INTO `user_info` VALUES ('1', '管理员', 'd3c59d25033dbf980d29554025c23a75','8d78869f470951332959580424d4bf4f', '0', 'admin');

十、关键的配置已经完成,剩下的就是把controller、service、mapper、和model层补齐就可以运行起来测试一下了,

当在浏览器里输入http://localhost:8080/时,因为还没有登陆,会跳转到

http://localhost:8080/login登陆页面

当输入用户名、密码检验登陆成功后,进入index.html页面

当输入http://localhost:8080/userInfo/userList时,因为没有权限,会跳转到403.html

当输入http://localhost:8080/userInfo/userAdd时,因为有添加的权限,会跳转到userInfoAdd.html页面

当输入http://localhost:8080/userInfo/userDel时,因没有权限,同样会跳转到403.html上

当登陆成功,被授权访问后,多次刷新,均没有再去查询数据库,输出sql语句,说明配置的缓存也已经生效了

 

文章篇幅有限,代码没有贴全,有兴趣的小伙伴可以到我的码云上云下载。

https://gitee.com/fox9916/shiroDemo.git

你可能感兴趣的:(spring)