1. 程式人生 > >springboot整合shiro+redis

springboot整合shiro+redis

之前整合過ssm+shiro+ehcache,也弄過ssh,這次把springboot+redis整合進去

 

對應的pom:

<!--shiro-->


		<!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-core -->
		<dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-core</artifactId>
			<version>1.2.2</version>
		</dependency>

		<!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-web -->
		<dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-web</artifactId>
			<version>1.2.2</version>
		</dependency>

		<!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-spring -->
		<dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-spring</artifactId>
			<version>1.2.2</version>
		</dependency>

		<!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-ehcache -->
		<dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-ehcache</artifactId>
			<version>1.2.2</version>
		</dependency>

		<!-- shiro+redis快取外掛 -->
		<dependency>
			<groupId>org.crazycake</groupId>
			<artifactId>shiro-redis</artifactId>
			<version>3.1.0</version>
		</dependency>

1.建立好對應的許可權角色表

2.建立shiroConfig,JedisConfig

由於shiro的資料存放於redis,因此,通過

@Value("${redis.spring.redis.host}")

來獲取yml配置的redis引數:


redis:
  spring:
    redis:
      database: 1
      host: 192.168.3.124
      port: 6379
      timeout: 5000

通過autowired自動注入,再使用org.crazycake.shiro輔助工具jar來整合redis。

但是:

在啟動專案時,springboot載入shiro的順序似乎是在載入yml配置檔案之前,因此,還得修改LifecycleBeanPostProcessorBean 為static方法!!!!

package com.pb.news.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Component
public class JedisConfig{


    @Value("${redis.spring.redis.host}")
    private String host;

    @Value("${redis.spring.redis.port}")
    private Integer port;

    public String getHost() {
        return host;
    }

    public void setHost(String host) {
        this.host = host;
    }

    public Integer getPort() {
        return port;
    }

    public void setPort(Integer port) {
        this.port = port;
    }


}


import com.pb.news.servlet.ShiroRealm;

import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
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.filter.authc.LogoutFilter;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
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 org.springframework.context.annotation.DependsOn;

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

import javax.servlet.Filter;


/**
 * shiro配置類
 */
@Configuration
public class ShiroConfig {
    /**
     * LifecycleBeanPostProcessor,這是個DestructionAwareBeanPostProcessor的子類,
     * 負責org.apache.shiro.util.Initializable型別bean的生命週期的,初始化和銷燬。
     * 主要是AuthorizingRealm類的子類,以及EhCacheManager類。
     */

    @Autowired
    private JedisConfig jedisConfig;


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

    /**
     * HashedCredentialsMatcher,這個類是為了對密碼進行編碼的,
     * 防止密碼在資料庫裡明碼儲存,當然在登陸認證的時候,
     * 這個類也負責對form裡輸入的密碼進行編碼。
     */
    @Bean(name = "hashedCredentialsMatcher")
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
        credentialsMatcher.setHashAlgorithmName("MD5");
        credentialsMatcher.setHashIterations(2);
        credentialsMatcher.setStoredCredentialsHexEncoded(true);
        return credentialsMatcher;
    }

    /**
     * ShiroRealm,這是個自定義的認證類,繼承自AuthorizingRealm,
     * 負責使用者的認證和許可權的處理,可以參考JdbcRealm的實現。
     */
    @Bean(name = "shiroRealm")
    @DependsOn("lifecycleBeanPostProcessor")
    public ShiroRealm shiroRealm() {
        ShiroRealm realm = new ShiroRealm();
//        realm.setCredentialsMatcher(hashedCredentialsMatcher());
        return realm;
    }

//    /**
//     * EhCacheManager,快取管理,使用者登陸成功後,把使用者資訊和許可權資訊快取起來,
//     * 然後每次使用者請求時,放入使用者的session中,如果不設定這個bean,每個請求都會查詢一次資料庫。
//     */
//    @Bean(name = "ehCacheManager")
//    @DependsOn("lifecycleBeanPostProcessor")
//    public EhCacheManager ehCacheManager() {
//        return new EhCacheManager();
//    }

    /**
     * SecurityManager,許可權管理,這個類組合了登陸,登出,許可權,session的處理,是個比較重要的類。
     * //
     */
    @Bean(name = "securityManager")
    public DefaultWebSecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 設定realm.
        securityManager.setRealm(shiroRealm());
        // 自定義快取實現 使用redis
        securityManager.setCacheManager(cacheManager());
        // 自定義session管理 使用redis
        securityManager.setSessionManager(sessionManager());
//        securityManager.setCacheManager(ehCacheManager());
        return securityManager;
    }

    /**
     * ShiroFilterFactoryBean,是個factorybean,為了生成ShiroFilter。
     * 它主要保持了三項資料,securityManager,filters,filterChainDefinitionManager。
     */
    @Bean(name = "shiroFilter")
    public ShiroFilterFactoryBean shiroFilterFactoryBean() {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager());

        Map<String, Filter> filters = new LinkedHashMap<String, Filter>();
        LogoutFilter logoutFilter = new LogoutFilter();
        logoutFilter.setRedirectUrl("/login");
//        filters.put("logout",null);
        shiroFilterFactoryBean.setFilters(filters);

        Map<String, String> filterChainDefinitionManager = new LinkedHashMap<String, String>();
        filterChainDefinitionManager.put("/logout", "logout");
        filterChainDefinitionManager.put("/user/**", "authc,roles[ROLE_USER]");
        filterChainDefinitionManager.put("/events/**", "authc,roles[ROLE_ADMIN]");
//        filterChainDefinitionManager.put("/user/edit/**", "authc,perms[user:edit]");// 這裡為了測試,固定寫死的值,也可以從資料庫或其他配置中讀取
        filterChainDefinitionManager.put("/**", "anon");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionManager);


        shiroFilterFactoryBean.setSuccessUrl("/");
        shiroFilterFactoryBean.setUnauthorizedUrl("/403");
        return shiroFilterFactoryBean;
    }

    /**
     * DefaultAdvisorAutoProxyCreator,Spring的一個bean,由Advisor決定對哪些類的方法進行AOP代理。
     */
    @Bean
    @ConditionalOnMissingBean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
        defaultAAP.setProxyTargetClass(true);
        return defaultAAP;
    }

    /**
     * AuthorizationAttributeSourceAdvisor,shiro裡實現的Advisor類,
     * 內部使用AopAllianceAnnotationsAuthorizingMethodInterceptor來攔截用以下註解的方法。
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
        AuthorizationAttributeSourceAdvisor aASA = new AuthorizationAttributeSourceAdvisor();
        aASA.setSecurityManager(securityManager());
        return aASA;
    }


    /**
     * cacheManager 快取 redis實現
     * 使用的是shiro-redis開源外掛
     *
     * @return
     */
    public RedisCacheManager cacheManager() {
        RedisCacheManager redisCacheManager = new RedisCacheManager();
        redisCacheManager.setRedisManager(redisManager());
        return redisCacheManager;
    }

    /**
     * 配置shiro redisManager
     * 使用的是shiro-redis開源外掛
     *
     * @return
     */
    public RedisManager redisManager() {
        RedisManager redisManager = new RedisManager();
        System.out.println(jedisConfig);
        redisManager.setHost("192.168.3.124:6379");
        redisManager.setPort(6379);
        //redisManager.setExpire(1800);// 配置快取過期時間
        redisManager.setTimeout(0);
        // redisManager.setPassword(password);
        return redisManager;
    }

    /**
     * Session Manager
     * 使用的是shiro-redis開源外掛
     */
    @Bean
    public DefaultWebSessionManager sessionManager() {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setSessionDAO(redisSessionDAO());
        return sessionManager;
    }

    /**
     * RedisSessionDAO shiro sessionDao層的實現 通過redis
     * 使用的是shiro-redis開源外掛
     */
    @Bean
    public RedisSessionDAO redisSessionDAO() {
        RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
        redisSessionDAO.setRedisManager(redisManager());
        return redisSessionDAO;
    }




}





3.建立ShiroRealm



import com.pb.news.dao.vo.UserMapperExt;
import com.pb.news.entity.User;
import com.pb.news.services.RoleService;
import com.pb.news.services.UserService;

import org.apache.commons.lang3.StringUtils;
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.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.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Created by cdyoue on 2016/10/21.
 */

public class ShiroRealm extends AuthorizingRealm {
    //private Logger logger =  LoggerFactory.getLogger(this.getClass());

    @Autowired
    private UserService userService;

    @Autowired
    private RoleService roleService;

    @Autowired
    private UserMapperExt userMapperExt;


    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        //logger.info("doGetAuthorizationInfo+"+principalCollection.toString());

        //username:principalCollection.getPrimaryPrincipal()
        User user = userService.getUserByUserName((String) principalCollection.getPrimaryPrincipal());

        User test2 = userMapperExt.test2();
        //List<Map<String,Object>> test = userMapperExt.test();


        //獲取role
        List<Map<String,Object>> roleList = new ArrayList<>();
        //roleList = roleService.getRolesByUser(user.getId());

        //獲取permission
        List<Map<String,Object>> permissionList = new ArrayList<>();
        permissionList = roleService.getRolesPermissionByUser(user.getId());

        //把principals放session中 key=userId value=principals
        SecurityUtils.getSubject().getSession().setAttribute(String.valueOf(user.getId()),SecurityUtils.getSubject().getPrincipals());

        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        //賦予角色
        //Set<String> roleNameSet = new HashSet<>();
        /*if(roleList.size() > 0){
            for (Iterator<Map<String, Object>> iterator = roleList.iterator(); iterator.hasNext(); ) {
                Map<String, Object> next = iterator.next();
                String roleName = (String) next.get("rname");
                if(StringUtils.isNoneBlank(roleName)){
                    info.addRole(roleName);
                }
            }
        }*/

        //賦予許可權和角色
        if(permissionList.size() > 0){
            for (Iterator<Map<String, Object>> iterator = permissionList.iterator(); iterator.hasNext(); ) {
                Map<String, Object> next = iterator.next();
                String roleName = (String) next.get("rname");
                String pname = (String) next.get("pname");
                if(StringUtils.isNoneBlank(roleName)){
                    info.addRole(roleName);
                }
                if(StringUtils.isNoneBlank(pname)){
                    info.addStringPermission(pname);
                }
            }
        }
       /* for(Permission permission:permissionService.getByUserId(user.getId())){
//            if(StringUtils.isNotBlank(permission.getPermCode()))
                info.addStringPermission(permission.getName());
        }*/

        //設定登入次數、時間
//        userService.updateUserLogin(user);
        return info;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        //logger.info("doGetAuthenticationInfo +"  + authenticationToken.toString());

        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        String userName=token.getUsername();
       // logger.info(userName+token.getPassword());

        User user = userService.getUserByUserName(token.getUsername());
        if (user != null) {
//            byte[] salt = Encodes.decodeHex(user.getSalt());
//            ShiroUser shiroUser=new ShiroUser(user.getId(), user.getLoginName(), user.getName());
            //設定使用者session
            Session session = SecurityUtils.getSubject().getSession();
            session.setAttribute("user", user);
            return new SimpleAuthenticationInfo(userName,user.getPassword(),getName());
        } else {
            return null;
        }
//        return null;
    }

}

4.對應的查詢角色以及許可權的sql

<?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.pb.news.dao.vo.UserMapperExt" >


  <resultMap id="BaseResultMap" type="com.pb.news.entity.User" >
    <id column="id" property="id" jdbcType="INTEGER" />
    <result column="username" property="username" jdbcType="VARCHAR" />
    <result column="password" property="password" jdbcType="VARCHAR" />
    <result column="address" property="address" jdbcType="VARCHAR" />
    <result column="tel" property="tel" jdbcType="VARCHAR" />
    <result column="name" property="name" jdbcType="VARCHAR" />
    <result column="register" property="register" jdbcType="TIMESTAMP" />
    <result column="money" property="money" jdbcType="INTEGER" />
    <result column="type" property="type" jdbcType="INTEGER" />
  </resultMap>


    <select id="selectRoleByUserId" resultType="java.util.HashMap" parameterType="java.lang.Integer">
      SELECT
      tu.username,
      tur.role_id,
      tr.id,
      tr.role_name as rname
      FROM
      USER tu
      LEFT JOIN user_role tur
      ON tu.id = tur.user_id
      LEFT JOIN role tr
      ON tur.role_id = tr.id
      WHERE 1=1 and tu.id = #{id,jdbcType=INTEGER}
  </select>


    <select id="selectRolePermissionByUserId" resultType="java.util.HashMap" parameterType="java.lang.Integer">
      SELECT
      tu.username,
      tur.role_id,
      tr.role_name as rname,
      tp.name as pname
      FROM
      USER tu
      LEFT JOIN user_role tur
      ON tu.id = tur.user_id
      LEFT JOIN role tr
      ON tur.role_id = tr.id
      LEFT JOIN role_permission trp
      ON trp.role_id = tur.role_id
      LEFT JOIN permission tp
      ON trp.permission_id = tp.id
      WHERE 1=1 and tu.id = #{id,jdbcType=INTEGER}
  </select>

    <select id="test" resultType="java.util.HashMap" >
      SELECT
      tu.username,
      tur.role_id,
      tr.id,
      tr.name as rname
      FROM
      USER tu
      LEFT JOIN user_role tur
      ON tu.id = tur.user_id
      LEFT JOIN role tr
      ON tur.role_id = tr.id


  </select>







    <select id="test2" resultMap="BaseResultMap"  >
        select
        *
        from user
        where id = 1
    </select>

</mapper>

 

參考資料:

https://blog.csdn.net/u012373815/article/details/57532292

https://blog.csdn.net/wuxuyang_7788/article/details/70141812