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

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/fox9916/article/details/80179775


一、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规则和访问权限。

三、项目结构

四、pom.xml

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

   <groupId>
com.gaox</groupId>
   <artifactId>
shiroDemo</artifactId>
   <version>
0.0.1-SNAPSHOT</version>
   <packaging>
jar</packaging>

   <name>
shiroDemo</name>
   <description>
Demo project for Spring Boot</description>

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

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

   <dependencies>
      <dependency>
         <groupId>
org.springframework.boot</groupId>
         <artifactId>
spring-boot-starter-web</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-thymeleaf</artifactId>
      </dependency>
      <dependency>
         <groupId>
net.sourceforge.nekohtml</groupId>
         <artifactId>
nekohtml</artifactId>
         <version>
1.9.22</version>
      </dependency>
     
<!--shiro start-->
      
<dependency>
         <groupId>
org.apache.shiro</groupId>
         <artifactId>
shiro-spring</artifactId>
         <version>
1.4.0</version>
      </dependency>
      <dependency>
         <groupId>
org.apache.shiro</groupId>
         <artifactId>
shiro-ehcache</artifactId>
         <version>
1.2.5</version>
      </dependency>
     
<!--shiro end-->
     
<dependency>
         <groupId>
mysql</groupId>
         <artifactId>
mysql-connector-java</artifactId>
         <scope>
runtime</scope>
      </dependency>
     
<!--mybatis start-->
     
<dependency>
         <groupId>
com.baomidou</groupId>
         <artifactId>
mybatis-plus</artifactId>
         <version>
2.2.0</version>
      </dependency>
      <dependency>
         <groupId>
org.mybatis.spring.boot</groupId>
         <artifactId>
mybatis-spring-boot-starter</artifactId>
         <version>
1.3.2</version>
      </dependency>
<!--mybatisend-->
  
</dependencies>

   <build>
      <finalName>
shiroDemo</finalName>

      <plugins>
         <plugin>
            <groupId>
org.springframework.boot</groupId>
            <artifactId>
spring-boot-maven-plugin</artifactId>
         </plugin>
      </plugins>

   </build>


</project>

五、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"?>
<ehcache name="shirocache">
    <diskStore path="java.io.tmpdir"/>
    <defaultCache
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            overflowToDisk="false"
            diskPersistent="false"
            diskExpiryThreadIntervalSeconds="120"
    />
    <!-- 授权缓存 -->
    <cache name="authorizationCache"
           maxEntriesLocalHeap="2000"
           eternal="false"
           timeToIdleSeconds="3600"
           timeToLiveSeconds="0"
           overflowToDisk="false"
           statistics="true">
    </cache>
    <!-- 认证缓存 -->
    <cache name="authenticationCache"
           maxEntriesLocalHeap="2000"
           eternal="false"
           timeToIdleSeconds="3600"
           timeToLiveSeconds="0"
           overflowToDisk="false"
           statistics="true">
    </cache>
</ehcache>

七、定义自己的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<String> roles=userInfoService.findRoleByUser(userInfo.getUsername());
        Set<String> 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,String> filterChainDefinitionMap = new LinkedHashMap<String,String>();
      // 配置不会被拦截的链接 顺序判断
      filterChainDefinitionMap.put("/static/**", "anon");
      //配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了
      filterChainDefinitionMap.put("/logout", "logout");
      //<!-- 过滤链定义,从上向下顺序执行,一般将/**放在最为下边 -->:这是一个坑呢,一不小心代码就不好使了;
      //<!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->
      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

猜你喜欢

转载自blog.csdn.net/fox9916/article/details/80179775