1. 程式人生 > 實用技巧 >SpringBoot整合Mybatis和Shiro(程式碼記錄)

SpringBoot整合Mybatis和Shiro(程式碼記錄)

本文為個人學習demo記錄隨筆,主要程式碼記錄

*Redis部分忽略,因為僅僅是整合,未測試

*內容中包含很多個人學習性程式碼

一、專案目錄

展開

二、詳細程式碼

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.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"
> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.1.RELEASE</version> <relativePath/> </parent
> <groupId>com.classic</groupId> <artifactId>demo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>demo</name> <description>Demo project for Spring Boot</description> <properties> <java.version
>1.8</java.version> <mybatis.version>2.1.3</mybatis.version> <shiro.version>1.5.3</shiro.version> <hikaricp.version>3.4.5</hikaricp.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <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> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> <!-- JDBC --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> <exclusions> <exclusion> <groupId>org.apache.tomcat</groupId> <artifactId>tomcat-jdbc</artifactId> </exclusion> </exclusions> </dependency> <!-- HikariCP --> <dependency> <groupId>com.zaxxer</groupId> <artifactId>HikariCP</artifactId> </dependency> <!-- MySQL --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!-- Mybatis --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>${mybatis.version}</version> </dependency> <!-- shiro --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>${shiro.version}</version> </dependency> <!-- Redis --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
View Code

application.properties

### 基本配置  ###
server.port=8080
server.servlet.context-path=/demo

### DataSource ###
spring.datasource.url=jdbc:mysql://localhost:3306/sourceplan?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&failOverReadOnly=false
spring.datasource.username=root
spring.datasource.password=pw123456
# 驅動
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
# 資料來源型別
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
# 連線池名稱
spring.datasource.hikari.pool-name=DateSourceHikariCP
# 等待連線池分配連線的最大時長(毫秒),超過這個時長還沒可用的連線則發生SQLException, 預設:30秒
spring.datasource.hikari.connection-timeout=30000
# 最小連線數
spring.datasource.hikari.minimum-idle=10
# 最大連線數
spring.datasource.hikari.maximum-pool-size=2000
# 自動提交
spring.datasource.hikari.auto-commit=true
# 連線超時的最大時長(毫秒),超時則被釋放(retired),預設:10分鐘
spring.datasource.hikari.idle-timeout=600000
# 連線的生命時長(毫秒),超時而且沒被使用則被釋放(retired),預設:30分鐘 1800000ms
spring.datasource.hikari.max-lifetime=1800000
# 連線測試
spring.datasource.hikari.connection-test-query=SELECT 1

### MyBatis 配置  ###
# 所有POJO類所在包路徑
#mybatis.type-aliases-package=com.test.pojo
# mapper對映檔案
mybatis.mapper-locations=classpath:mapper/*.xml

### Redis pool 配置  ###
# Redis資料庫索引(預設為0,最大15) 
#spring.redis.database=0
# Redis伺服器地址
#spring.redis.host=192.168.0.24
# Redis伺服器連線埠
#spring.redis.port=6379
# Redis伺服器連線密碼(預設為空)
#spring.redis.password=
# 連線池最大連線數(使用負值表示沒有限制)
#spring.redis.pool.max-active=200
# 連線池最大阻塞等待時間(使用負值表示沒有限制) 
#spring.redis.pool.max-wait=-1
# 連線池中的最大空閒連線
#spring.redis.pool.max-idle=10
# 連線池中的最小空閒連線
#spring.redis.pool.min-idle=0
# 連線超時時間(毫秒) 
#spring.redis.timeout=1000
View Code

啟動類:DemoApplication.java

package com.classic.app;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@SpringBootApplication
@ComponentScan(basePackages = {"com.classic"})
@MapperScan("com.classic.dao")
@EnableTransactionManagement // 啟註解事務管理,等同於xml配置方式的 <tx:annotation-driven />
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

}
View Code

建表語句(表中均為模擬資料)

SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- Table structure for permission
-- ----------------------------
DROP TABLE IF EXISTS `permission`;
CREATE TABLE `permission` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `permission_code` varchar(20) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of permission
-- ----------------------------
INSERT INTO `permission` VALUES ('1', 'query');
INSERT INTO `permission` VALUES ('2', 'update');
INSERT INTO `permission` VALUES ('3', 'insert');
INSERT INTO `permission` VALUES ('4', 'delete');

-- ----------------------------
-- Table structure for role
-- ----------------------------
DROP TABLE IF EXISTS `role`;
CREATE TABLE `role` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `role_code` varchar(100) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of role
-- ----------------------------
INSERT INTO `role` VALUES ('1', 'ADMIN');
INSERT INTO `role` VALUES ('2', 'USER');

-- ----------------------------
-- Table structure for role_to_permission
-- ----------------------------
DROP TABLE IF EXISTS `role_to_permission`;
CREATE TABLE `role_to_permission` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `role_id` int(11) DEFAULT NULL,
  `permission_id` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of role_to_permission
-- ----------------------------
INSERT INTO `role_to_permission` VALUES ('1', '1', '1');
INSERT INTO `role_to_permission` VALUES ('2', '1', '2');
INSERT INTO `role_to_permission` VALUES ('3', '1', '3');
INSERT INTO `role_to_permission` VALUES ('4', '1', '4');
INSERT INTO `role_to_permission` VALUES ('5', '2', '1');

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `username` varchar(30) DEFAULT NULL,
  `password` varchar(30) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES ('1', 'zhousjcn', 'pw123456');
INSERT INTO `user` VALUES ('2', 'zhousj', 'pw123456');

-- ----------------------------
-- Table structure for user_role
-- ----------------------------
DROP TABLE IF EXISTS `user_role`;
CREATE TABLE `user_role` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `user_id` bigint(20) DEFAULT NULL,
  `role_id` bigint(20) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of user_role
-- ----------------------------
INSERT INTO `user_role` VALUES ('1', '1', '1');
INSERT INTO `user_role` VALUES ('2', '2', '2');
View Code

entity

User.java

package com.classic.entity;

import java.util.Set;

public class User {
    
    private Long id;
    private String username;
    private String password;
    
    /*
     * 使用者對應角色集合
     */
    private Set<Role> roleSet;
    
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public Set<Role> getRoleSet() {
        return roleSet;
    }
    public void setRoleSet(Set<Role> roleSet) {
        this.roleSet = roleSet;
    }
    
    
}
View Code

Role.java

package com.classic.entity;

import java.util.Set;

public class Role {
    
    private Long id;
    private String roleCode;
    
    /**
     * 角色對應許可權集合
     */
    private Set<Permission> permissionSet;
    
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getRoleCode() {
        return roleCode;
    }
    public void setRoleCode(String roleCode) {
        this.roleCode = roleCode;
    }
    public Set<Permission> getPermissionSet() {
        return permissionSet;
    }
    public void setPermissionSet(Set<Permission> permissionSet) {
        this.permissionSet = permissionSet;
    }
    
}
View Code

Permission.java

package com.classic.entity;

public class Permission {
    
    private Long id;
    private String permissionCode;
    
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getPermissionCode() {
        return permissionCode;
    }
    public void setPermissionCode(String permissionCode) {
        this.permissionCode = permissionCode;
    }
    
    
}
View Code

AbstractDao.java

package com.classic.commons;

/**
 * 一些規定的dao層介面
 * @author ...
 *
 */
public abstract class AbstractDao {
    
    
    
}
View Code

configure包

DemoExceptionHandler.java

package com.classic.configure;

import org.apache.shiro.authz.UnauthenticatedException;
import org.apache.shiro.authz.UnauthorizedException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

/**
 * 異常捕捉類
 */
@ControllerAdvice
public class DemoExceptionHandler {

    private static Logger logger = LoggerFactory.getLogger(DemoExceptionHandler.class);
    
    @ExceptionHandler
    @ResponseBody
    public String unauthenticatedExceptionHandler(UnauthenticatedException e) {
        logger.warn(e.getMessage(), e);
        return "使用者名稱或密碼錯誤";
    }
    
    @ExceptionHandler
    @ResponseBody
    public String unauthorizedExceptionHandler(UnauthorizedException e) {
        logger.warn(e.getMessage(), e);
        return "許可權不足";
    }
    
    @ExceptionHandler
    @ResponseBody
    public String exceptionHandler(Exception e) {
        logger.error(e.getMessage(), e);
        return "未知異常";
    }
    
}
View Code

ExceptionMessageType.java

package com.classic.configure;

public enum ExceptionMessageType {
    
    AUTHENTICATION_EXCEPTION,
    AUTHORIZATION_EXCEPTION,
    UNAUTHENTICATED_EXCEPTION,    // 賬號密碼錯誤
    UNAUTHORIZED_EXCEPTION    // 許可權不足
    
}
View Code

RedisConfig.java

package com.classic.configure;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;

@Configuration
public class RedisConfig {
    
    @Bean
//    @SuppressWarnings("all")
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
        template.setConnectionFactory(factory);
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, DefaultTyping.NON_FINAL, As.PROPERTY);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        // key採用String的序列化方式
        template.setKeySerializer(stringRedisSerializer);
        // hash的key也採用String的序列化方式
        template.setHashKeySerializer(stringRedisSerializer);
        // value序列化方式採用jackson
        template.setValueSerializer(jackson2JsonRedisSerializer);
        // hash的value序列化方式採用jackson
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }
    
}
View Code

ShiroConfig.java

package com.classic.configure;

import java.util.HashMap;
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.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.classic.realm.UserRealm;

@Configuration
public class ShiroConfig {
    
    @Autowired
    private UserRealm userRealm;
    
    /**
     * 開啟Shiro的註解(如@RequiresRoles,@RequiresPermissions)
     * 配置以下兩個bean(DefaultAdvisorAutoProxyCreator和AuthorizationAttributeSourceAdvisor)即可實現此功能
     * @return
     */
    @Bean
    @ConditionalOnMissingBean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator daapc = new DefaultAdvisorAutoProxyCreator();
        daapc.setProxyTargetClass(true);
        return daapc;
    }

    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }
    
    //許可權管理,配置主要是Realm的管理認證
    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(userRealm);
        return securityManager;
    }
    
  //Filter工廠,設定對應的過濾條件和跳轉條件
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        Map<String, String> map = new HashMap<>();
        //登出
        map.put("/logout", "logout");
        //對所有使用者認證
        map.put("/**", "authc");
        // 如果未登入這跳轉登入
        shiroFilterFactoryBean.setLoginUrl("/user/login");
        //首頁
        shiroFilterFactoryBean.setSuccessUrl("/index");
        //錯誤頁面,認證不通過跳轉
        shiroFilterFactoryBean.setUnauthorizedUrl("/error");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
        return shiroFilterFactoryBean;
    }

}
View Code

realm包

UserRealm.java

package com.classic.realm;

import java.util.Set;

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.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.classic.entity.Permission;
import com.classic.entity.Role;
import com.classic.entity.User;
import com.classic.service.UserService;

@Component
public class UserRealm extends AuthorizingRealm {

    @Autowired
    private UserService userService;
    
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        Object primaryPrincipal = principals.getPrimaryPrincipal();
        if (primaryPrincipal == null) {
            return null;
        }
        String principal = primaryPrincipal.toString();
        User user = userService.queryUserByUsername(principal);
        if (user == null) {
            return null;
        }
        SimpleAuthorizationInfo sai = new SimpleAuthorizationInfo();
        Set<Role> roleSet = user.getRoleSet();
        if (roleSet != null && roleSet.size() > 0) {
            for (Role role : roleSet) {
                // 新增角色
                sai.addRole(role.getRoleCode());
                // 新增許可權
                Set<Permission> permissionSet = role.getPermissionSet();
                if (permissionSet != null && permissionSet.size() > 0) {
                    for (Permission permission : permissionSet) {
                        sai.addStringPermission(permission.getPermissionCode());
                    }
                }
            }
        }
        return sai;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        //加這一步的目的是在Post請求的時候會先進認證,然後在到請求
        Object principalObj = token.getPrincipal();
        if (principalObj == null) {
            return null;
        }
        //獲取使用者資訊
        String principal = principalObj.toString();
        User user = userService.queryUserByUsername(principal);
        if (user == null) {
            //這裡返回後會報出對應異常
            return null;
        } else {
            //這裡驗證authenticationToken和simpleAuthenticationInfo的資訊
            SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(principal, user.getPassword().toString(), getName());
            return simpleAuthenticationInfo;
        }
    }

}
View Code

util包

RedisUtil.java

package com.classic.util;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

/**
 * Redis工具類
 */
@Component
public final class RedisUtil {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    // =============================common============================
    /**
     * 指定快取失效時間
     * @param key 鍵
     * @param time 時間(秒)
     * @return
     */
    public boolean expire(String key, long time) {
        try {
            if (time > 0) {
                redisTemplate.expire(key, time, TimeUnit.SECONDS);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 根據key 獲取過期時間
     * @param key 鍵 不能為null
     * @return 時間(秒) 返回0代表為永久有效
     */
    public long getExpire(String key) {
        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
    }

    /**
     * 判斷key是否存在
     * @param key 鍵
     * @return true 存在 false不存在
     */
    public boolean hasKey(String key) {
        try {
            return redisTemplate.hasKey(key);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 刪除快取
     * @param key 可以傳一個值 或多個
     */
    @SuppressWarnings("unchecked")
    public void del(String... key) {
        if (key != null && key.length > 0) {
            if (key.length == 1) {
                redisTemplate.delete(key[0]);
            } else {
                redisTemplate.delete(CollectionUtils.arrayToList(key));
            }
        }
    }

    // ============================String=============================
    /**
     * 普通快取獲取
     * @param key 鍵
     * @return*/
    public Object get(String key) {
        return key == null ? null : redisTemplate.opsForValue().get(key);
    }

    /**
     * 普通快取放入
     * @param key 鍵
     * @param value 值
     * @return true成功 false失敗
     */
    public boolean set(String key, Object value) {
        try {
            redisTemplate.opsForValue().set(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }

    }

    /**
     * 普通快取放入並設定時間
     * @param key 鍵
     * @param value 值
     * @param time 時間(秒) time要大於0 如果time小於等於0 將設定無限期
     * @return true成功 false 失敗
     */
    public boolean set(String key, Object value, long time) {
        try {
            if (time > 0) {
                redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
            } else {
                set(key, value);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 遞增
     * @param key 鍵
     * @param delta 要增加幾(大於0)
     * @return
     */
    public long incr(String key, long delta) {
        if (delta < 0) {
            throw new RuntimeException("遞增因子必須大於0");
        }
        return redisTemplate.opsForValue().increment(key, delta);
    }

    /**
     * 遞減
     * @param key 鍵
     * @param delta 要減少幾(小於0)
     * @return
     */
    public long decr(String key, long delta) {
        if (delta < 0) {
            throw new RuntimeException("遞減因子必須大於0");
        }
        return redisTemplate.opsForValue().increment(key, -delta);
    }

    // ================================Map=================================
    /**
     * HashGet
     * @param key 鍵 不能為null
     * @param item 項 不能為null
     * @return*/
    public Object hget(String key, String item) {
        return redisTemplate.opsForHash().get(key, item);
    }

    /**
     * 獲取hashKey對應的所有鍵值
     * @param key 鍵
     * @return 對應的多個鍵值
     */
    public Map<Object, Object> hmget(String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    /**
     * HashSet
     * @param key 鍵
     * @param map 對應多個鍵值
     * @return true 成功 false 失敗
     */
    public boolean hmset(String key, Map<String, Object> map) {
        try {
            redisTemplate.opsForHash().putAll(key, map);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * HashSet 並設定時間
     * @param key 鍵
     * @param map 對應多個鍵值
     * @param time 時間(秒)
     * @return true成功 false失敗
     */
    public boolean hmset(String key, Map<String, Object> map, long time) {
        try {
            redisTemplate.opsForHash().putAll(key, map);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 向一張hash表中放入資料,如果不存在將建立
     * @param key 鍵
     * @param item 項
     * @param value 值
     * @return true 成功 false失敗
     */
    public boolean hset(String key, String item, Object value) {
        try {
            redisTemplate.opsForHash().put(key, item, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 向一張hash表中放入資料,如果不存在將建立
     * @param key 鍵
     * @param item 項
     * @param value 值
     * @param time 時間(秒) 注意:如果已存在的hash表有時間,這裡將會替換原有的時間
     * @return true 成功 false失敗
     */
    public boolean hset(String key, String item, Object value, long time) {
        try {
            redisTemplate.opsForHash().put(key, item, value);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 刪除hash表中的值
     * @param key 鍵 不能為null
     * @param item 項 可以使多個 不能為null
     */
    public void hdel(String key, Object... item) {
        redisTemplate.opsForHash().delete(key, item);
    }

    /**
     * 判斷hash表中是否有該項的值
     * @param key 鍵 不能為null
     * @param item 項 不能為null
     * @return true 存在 false不存在
     */
    public boolean hHasKey(String key, String item) {
        return redisTemplate.opsForHash().hasKey(key, item);
    }

    /**
     * hash遞增 如果不存在,就會建立一個 並把新增後的值返回
     * @param key 鍵
     * @param item 項
     * @param by 要增加幾(大於0)
     * @return
     */
    public double hincr(String key, String item, double by) {
        return redisTemplate.opsForHash().increment(key, item, by);
    }

    /**
     * hash遞減
     * @param key 鍵
     * @param item 項
     * @param by 要減少記(小於0)
     * @return
     */
    public double hdecr(String key, String item, double by) {
        return redisTemplate.opsForHash().increment(key, item, -by);
    }

    // ============================set=============================
    /**
     * 根據key獲取Set中的所有值
     * @param key 鍵
     * @return
     */
    public Set<Object> sGet(String key) {
        try {
            return redisTemplate.opsForSet().members(key);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 根據value從一個set中查詢,是否存在
     * @param key 鍵
     * @param value 值
     * @return true 存在 false不存在
     */
    public boolean sHasKey(String key, Object value) {
        try {
            return redisTemplate.opsForSet().isMember(key, value);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 將資料放入set快取
     * @param key 鍵
     * @param values 值 可以是多個
     * @return 成功個數
     */
    public long sSet(String key, Object... values) {
        try {
            return redisTemplate.opsForSet().add(key, values);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 將set資料放入快取
     * @param key 鍵
     * @param time 時間(秒)
     * @param values 值 可以是多個
     * @return 成功個數
     */
    public long sSetAndTime(String key, long time, Object... values) {
        try {
            Long count = redisTemplate.opsForSet().add(key, values);
            if (time > 0)
                expire(key, time);
            return count;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 獲取set快取的長度
     * @param key 鍵
     * @return
     */
    public long sGetSetSize(String key) {
        try {
            return redisTemplate.opsForSet().size(key);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 移除值為value的
     * @param key 鍵
     * @param values 值 可以是多個
     * @return 移除的個數
     */
    public long setRemove(String key, Object... values) {
        try {
            Long count = redisTemplate.opsForSet().remove(key, values);
            return count;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    // ===============================list=================================

    /**
     * 獲取list快取的內容
     * @param key 鍵
     * @param start 開始
     * @param end 結束 0 到 -1代表所有值
     * @return
     */
    public List<Object> lGet(String key, long start, long end) {
        try {
            return redisTemplate.opsForList().range(key, start, end);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 獲取list快取的長度
     * @param key 鍵
     * @return
     */
    public long lGetListSize(String key) {
        try {
            return redisTemplate.opsForList().size(key);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 通過索引 獲取list中的值
     * @param key 鍵
     * @param index 索引 index>=0時, 0 表頭,1 第二個元素,依次類推;index<0時,-1,表尾,-2倒數第二個元素,依次類推
     * @return
     */
    public Object lGetIndex(String key, long index) {
        try {
            return redisTemplate.opsForList().index(key, index);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 將list放入快取
     * @param key 鍵
     * @param value 值
     * @param time 時間(秒)
     * @return
     */
    public boolean lSet(String key, Object value) {
        try {
            redisTemplate.opsForList().rightPush(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 將list放入快取
     * @param key 鍵
     * @param value 值
     * @param time 時間(秒)
     * @return
     */
    public boolean lSet(String key, Object value, long time) {
        try {
            redisTemplate.opsForList().rightPush(key, value);
            if (time > 0)
                expire(key, time);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 將list放入快取
     * @param key 鍵
     * @param value 值
     * @param time 時間(秒)
     * @return
     */
    public boolean lSet(String key, List<Object> value) {
        try {
            redisTemplate.opsForList().rightPushAll(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 將list放入快取
     * 
     * @param key 鍵
     * @param value 值
     * @param time 時間(秒)
     * @return
     */
    public boolean lSet(String key, List<Object> value, long time) {
        try {
            redisTemplate.opsForList().rightPushAll(key, value);
            if (time > 0)
                expire(key, time);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 根據索引修改list中的某條資料
     * @param key 鍵
     * @param index 索引
     * @param value 值
     * @return
     */
    public boolean lUpdateIndex(String key, long index, Object value) {
        try {
            redisTemplate.opsForList().set(key, index, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 移除N個值為value
     * @param key 鍵
     * @param count 移除多少個
     * @param value 值
     * @return 移除的個數
     */
    public long lRemove(String key, long count, Object value) {
        try {
            Long remove = redisTemplate.opsForList().remove(key, count, value);
            return remove;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }
}
View Code

dao層

UserMapper.java

package com.classic.dao;

import java.util.List;

import org.apache.ibatis.annotations.Select;

import com.classic.entity.User;

public interface UserMapper {
    
    @Select("select * from user")
    List<User> queryAll();
    
    @Select(
            "select id,username,password from user "
            + "where username = #{username}"
            )
    User queryUserByUsername(String username);
    
}
View Code

UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.classic.dao.UserMapper">
   
    <!-- <select id="queryAll" resultType="com.classic.entity.User">
        select * from user
    </select> -->
    
</mapper>
View Code

RoleMapper.java

package com.classic.dao;

import java.util.Set;

import org.apache.ibatis.annotations.Select;

import com.classic.entity.Role;

public interface RoleMapper {
    
    Set<Role> queryRolesByIds(Set<Long> ids);

    @Select("select role_id from user_role where user_id=#{userId}")
    Set<Long> queryRoleIdsByUserId(Long userId);
    
    
}
View Code

RoleMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.classic.dao.RoleMapper">
       
       <sql id="selectAllColumnSql">
               select id,role_code roleCode from role
       </sql>
       
    <select id="queryRolesByIds" resultType="com.classic.entity.Role">
        <include refid="selectAllColumnSql" />
        where id in 
        <foreach collection="ids" item="id" index="index" open="(" close=")" separator=",">
                #{id}
        </foreach>
    </select>
    
</mapper>
View Code

PermissionMapper.java

package com.classic.dao;

import java.util.Set;

import org.apache.ibatis.annotations.Select;

import com.classic.entity.Permission;

public interface PermissionMapper {
    
    Set<Permission> queryPermissionsByIds(Set<Integer> ids);
    
    @Select("select permission_id from role_to_permission where role_id=#{roleId}")
    Set<Integer> queryPermissionIdsByRoleId(Long roleId);
    
}
View Code

PermissionMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.classic.dao.PermissionMapper">
       
       <sql id="selectAllColumnSql">
               select id,permission_code permissionCode from permission
       </sql>
       
    <select id="queryPermissionsByIds" resultType="com.classic.entity.Permission">
        <include refid="selectAllColumnSql"/>
        where id in 
        <foreach collection="ids" item="id" index="index" open="(" close=")" separator=",">
                #{id}
        </foreach>
    </select>
    
</mapper>
View Code

service層

UserService.java

package com.classic.service;

import java.util.List;

import com.classic.entity.User;

public interface UserService {

    List<User> queryAll();
    
    User queryUserByUsername(String username);
    
}
View Code

UserServiceImp.java

package com.classic.service;

import java.util.List;
import java.util.Set;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.classic.dao.UserMapper;
import com.classic.entity.Role;
import com.classic.entity.User;

@Service
public class UserServiceImp implements UserService {

    @Autowired
    private UserMapper userMapper;
    
    @Autowired
    private RoleService roleService;
    
    @Override
    public List<User> queryAll() {
        return userMapper.queryAll();
    }

    @Override
    public User queryUserByUsername(String username) {
        User user = userMapper.queryUserByUsername(username);
        if (user != null) {
            Long userId = user.getId();
            Set<Role> roleSet = roleService.queryRolesByUserId(userId);
            user.setRoleSet(roleSet);
        }
        return user;
    }
    
}
View Code

RoleService.java

package com.classic.service;

import java.util.Set;

import com.classic.entity.Role;

public interface RoleService {
    
    Set<Role> queryRolesByUserId(Long userId);
    
}
View Code

RoleServiceImpl.java

package com.classic.service;

import java.util.Set;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.classic.dao.RoleMapper;
import com.classic.entity.Role;

@Service
public class RoleServiceImpl implements RoleService {

    @Autowired
    private RoleMapper roleMapper;
    
    @Autowired
    private PermissionService permissionService;
    
    @Override
    public Set<Role> queryRolesByUserId(Long userId) {
        Set<Long> ids = roleMapper.queryRoleIdsByUserId(userId);
        if (ids != null && ids.size() > 0) {
            Set<Role> roleSet = roleMapper.queryRolesByIds(ids);
            if (roleSet != null && roleSet.size() > 0) {
                for (Role role : roleSet) {
                    Long roleId = role.getId();
                    role.setPermissionSet(permissionService.queryPermissionsByRoleId(roleId));
                }
            }
            return roleSet;
        }
        return null;
    }
    
}
View Code

PermissionService.java

package com.classic.service;

import java.util.Set;

import com.classic.entity.Permission;

public interface PermissionService {
    
    Set<Permission> queryPermissionsByRoleId(Long roleId);
    
}
View Code

PermissionServiceImpl.java

package com.classic.service;

import java.util.Set;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.classic.dao.PermissionMapper;
import com.classic.entity.Permission;

@Service
public class PermissionServiceImpl implements PermissionService {

    @Autowired
    private PermissionMapper permissionMapper;
    
    @Override
    public Set<Permission> queryPermissionsByRoleId(Long roleId) {
        Set<Integer> ids = permissionMapper.queryPermissionIdsByRoleId(roleId);
        if (ids != null && ids.size() > 0) {
            return permissionMapper.queryPermissionsByIds(ids);
        }
        return null;
    }

}
View Code

Controller層(僅提供測試介面)

UserController.java

package com.classic.controller;

import java.util.List;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.classic.entity.User;
import com.classic.service.UserService;

@RestController
@RequestMapping("/user")
public class UserController {

    private static final Logger logger = LoggerFactory.getLogger(UserController.class);
    
    @Autowired
    private UserService userService;
    
    @GetMapping("/queryAll")
    public List<User> queryAll() {
        return userService.queryAll();
    }
    
    @GetMapping("/queryByUsername")
    public User queryByUsername(String username) {
        return userService.queryUserByUsername(username);
    }
    
    @RequestMapping("/login")
    public String userLogin(User user) {
        //新增使用者認證資訊
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken usernamePasswordToken = 
                new UsernamePasswordToken(user.getUsername(), user.getPassword());
        try {
            //進行驗證,這裡可以捕獲異常,然後返回對應資訊
            subject.login(usernamePasswordToken);
//            subject.checkRole("ADMIN");
//            subject.checkPermissions("query", "add");
        } catch (AuthenticationException e) {
            logger.error(e.getMessage(), e);
            return "賬號或密碼錯誤!";
        } catch (AuthorizationException e) {
            logger.error(e.getMessage(), e);
            return "沒有許可權";
        }
        return "登入成功";
    }
    
    @RequestMapping("/index")
    @RequiresRoles(value={"ADMIN","USER"},logical = Logical.OR)
    @RequiresPermissions(value={"query"},logical = Logical.OR)
    public String indexTest() {
        return "Index";
    }
    
    @RequestMapping("/logout")
    public String userLogout() {
        Subject subject = SecurityUtils.getSubject();
        subject.logout();
        return "登出成功";
    }
    
}
View Code

參考連結:

https://www.jianshu.com/p/7f724bec3dc3

和許可權的話無法捕捉異常,從而無法正確的返回給前端錯誤資訊,所以我加了一個類用於攔截異常,具體程式碼如下