SpringBoot学习-(二十五)SpringBoot整合Shiro(详细版本)

整合内容包括

  • 自定义realm,实现认证和授权
  • 自定义加密,实现密码加密验证
  • 自定义Cachemanager、Cache,实现Shiro的cache管理,存储在redis中
  • 自定义SessionManager、SessionDao、SessionIdCookie,实现Shiro的session管理,存储在redsi中
  • 自定义RememberMeManager、RemeberMeCookie,实现Shiro记住我的功能

添加maven依赖

<!-- spring整合shiro -->
<!-- maven会自动添加shiro-core,shiro-web依赖 -->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.3.2</version>
</dependency>

<!-- redis相关 -->
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
</dependency>

<!-- 使用@Slf4j注解 -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>

Shiro配置

package com.ahut.config;

import com.ahut.shiro.MyRealm;
import com.ahut.shiro.RedisShiroCacheManager;
import com.ahut.shiro.RedisShiroSessionDao;
import com.ahut.utils.ShiroUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;

/**
 * @author cheng
 * @className: ShiroConfig
 * @description: shiro配置
 * @dateTime 2018/4/18 15:38
 */
@Configuration
@Slf4j
public class ShiroConfig {

}

Shiro工具类

package com.ahut.utils;

import com.ahut.entity.User;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author cheng
 * @className: ShiroUtil
 * @description: 管理shiro session的工具类
 * @dateTime 2018/4/19 10:15
 */
public class ShiroUtil {

    /**
     * 日志管理
     */
    private static Logger log = LoggerFactory.getLogger(ShiroUtil.class);
    /**
     * 当前用户
     */
    private static final String CURRENT_USER = "CURRENT_USER";
    /**
     * shiro加密算法
     */
    public static final String HASH_ALGORITHM_NAME = "md5";
    /**
     * 散列次数
     */
    public static final int HASH_ITERATIONS = 2;
    /**
     * 全局session过期时间
     */
    public static final int GLOBAL_SESSION_TIMEOUT = 60000;
    /**
     * 自定义shiro session的cookie名称
     */
    public static final String SESSIONID_COOKIE_NAME = "SHIRO_SESSION_ID";
    /**
     * 自定义remeber me的cookie名称
     */
    public static final String REMEBER_ME_COOKIE_NAME = "REMEBER_ME";
    /**
     * shiro session前缀
     */
    public static final String SHIRO_SESSION_PREFIX = "shiro_session:";
    /**
     * shiro cache前缀
     */
    public static final String SHIRO_CACHE_PREFIX = "shiro_cache:";
    /**
     * shiro session过期时间-秒
     */
    public static final int EXPIRE_SECONDS = 60;

    /**
     * @description: 私有化构造函数
     * @author cheng
     * @dateTime 2018/4/19 10:15
     */
    private ShiroUtil() {
    }

    /**
     * @description: 获取session
     * @author cheng
     * @dateTime 2018/4/19 10:38
     */
    public static Session getSession() {
        Session session = null;
        try {
            Subject currentUser = SecurityUtils.getSubject();
            session = currentUser.getSession();
        } catch (Exception e) {
            log.warn("获取shiro当前用户的session时发生了异常", e);
            throw e;
        }
        return session;
    }

    /**
     * @description: 将数据放到shiro session中
     * @author cheng
     * @dateTime 2018/4/19 10:45
     */
    public static void setAttribute(Object key, Object value) {
        try {
            Session session = getSession();
            session.setAttribute(key, value);
        } catch (Exception e) {
            log.warn("将一些数据放到Shiro Session中时发生了异常", e);
            throw e;
        }
    }

    /**
     * @description: 获取shiro session中的数据
     * @author cheng
     * @dateTime 2018/4/19 10:48
     */
    public static Object getAttribute(Object key) {
        Object value = null;
        try {
            Session session = getSession();
            value = session.getAttribute(key);
        } catch (Exception e) {
            log.warn("获取shiro session中的数据时发生了异常", e);
            throw e;
        }
        return value;
    }

    /**
     * @description: 删除shiro session中的数据
     * @author cheng
     * @dateTime 2018/4/19 10:51
     */
    public static void removeAttribute(Object key) {
        try {
            Session session = getSession();
            session.removeAttribute(key);
        } catch (Exception e) {
            log.warn("删除shiro session中的数据时发生了异常", e);
            throw e;
        }
    }

    /**
     * @description: 设置当前用户
     * @author cheng
     * @dateTime 2018/4/19 10:59
     */
    public static void setCurrentUser(Object user) {
        setAttribute(CURRENT_USER, user);
    }

    /**
     * @description: 获取当前用户
     * @author cheng
     * @dateTime 2018/4/19 10:59
     */
    public static User getCurrentUser() {
        User user = (User) getAttribute(CURRENT_USER);
        return user;
    }

    /**
     * @description:删除当前用户
     * @author cheng
     * @dateTime 2018/4/19 10:59
     */
    public static void removeCurrentUser() {
        removeAttribute(CURRENT_USER);
    }

    /**
     * @description: 加密密码
     * @author cheng
     * @dateTime 2018/4/23 15:01
     */
    public static String encrypt(String password, String salt) {
        Md5Hash md5Hash = new Md5Hash(password, salt, HASH_ITERATIONS);
        return md5Hash.toString();
    }

}

配置redis,请看我的这篇博客:springboot整合redis
redis工具类

package com.ahut.utils;

import org.apache.commons.lang3.ArrayUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.Set;

/**
 * @author cheng
 * @className: RedisUtil
 * @description: redis操作工具类
 * @dateTime 2018/4/23 16:12
 */
 // 因为工具类中的JedisPool 使用了spring注入,所以该工具类也要加入IOC容器
@Component
public class RedisUtil {

    /**
     * jedisPool
     */
    private static JedisPool jedisPool;

    /**
     * @description: 静态字段, 通过set注入jedispool
     * @author cheng
     * @dateTime 2018/4/24 9:45
     */
    @Autowired
    public void setJedisPool(JedisPool jedisPool) {
        RedisUtil.jedisPool = jedisPool;
    }

    /**
     * @description: 私有化构造函数
     * @author cheng
     * @dateTime 2018/4/23 16:12
     */
    private RedisUtil() {
    }

    /**
     * @description: 获取jedis
     * @author cheng
     * @dateTime 2018/4/24 9:47
     */
    public static Jedis getJedis() {
        return jedisPool.getResource();
    }

    /**
     * @description: 保存到redis
     * @author cheng
     * @dateTime 2018/4/24 10:04
     */
    public static void set(byte[] key, byte[] value) {
        Jedis jedis = getJedis();
        try {
            jedis.set(key, value);
        } finally {
            jedis.close();
        }
    }

    /**
     * @description: 从redis中获取
     * @author cheng
     * @dateTime 2018/4/24 10:11
     */
    public static byte[] get(byte[] key) {
        Jedis jedis = getJedis();
        try {
            return jedis.get(key);
        } finally {
            jedis.close();
        }
    }

    /**
     * @description: 从redis中删除
     * @author cheng
     * @dateTime 2018/4/24 10:17
     */
    public static void del(byte[] key) {
        Jedis jedis = getJedis();
        try {
            jedis.del(key);
        } finally {
            jedis.close();
        }
    }

    /**
     * @description: 依据前缀删除key
     * @author cheng
     * @dateTime 2018/4/24 16:48
     */
    public static void delByPrefix(String keyPrefix) {
        keyPrefix = keyPrefix + "*";
        Jedis jedis = getJedis();
        try {
            Set<byte[]> keyByteArraySet = jedis.keys(keyPrefix.getBytes());
            for (byte[] keyByteArray : keyByteArraySet) {
                jedis.del(keyByteArray);
            }
        } finally {
            jedis.close();
        }
    }

    /**
     * @description: 设置redis过期时间
     * @author cheng
     * @dateTime 2018/4/24 10:21
     */
    public static void expire(byte[] key, int seconds) {
        Jedis jedis = getJedis();
        try {
            jedis.expire(key, seconds);
        } finally {
            jedis.close();
        }
    }

    /**
     * @description: 从redis中获取指定前缀的key
     * @author cheng
     * @dateTime 2018/4/24 10:25
     */
    public static Set<byte[]> keys(String shiroSessionPrefix) {
        shiroSessionPrefix = shiroSessionPrefix + "*";
        Jedis jedis = getJedis();
        try {
            return jedis.keys(shiroSessionPrefix.getBytes());
        } finally {
            jedis.close();
        }
    }
}

自定义Realm

package com.ahut.shiro;

import com.ahut.common.ApiResponse;
import com.ahut.entity.User;
import com.ahut.enums.UserStatusEnum;
import com.ahut.service.UserService;
import com.ahut.utils.ShiroUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @author cheng
 * @className: MyRealm
 * @description: 自定义realm
 * @dateTime 2018/4/18 15:40
 */
@Slf4j
public class MyRealm extends AuthorizingRealm {

    /**
     * 用户业务逻辑,因为使用的spring的自动装配,
     * 所以MyRealm 也要添加到IOC容器中,不然会出现userService为null
     */
    @Autowired
    private UserService userService;

    /**
     * @description: 用于认证
     * @author cheng
     * @dateTime 2018/4/18 15:42
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        UsernamePasswordToken newToken = (UsernamePasswordToken) token;
        // 获取用户账号
        String userAccount = newToken.getUsername();
        log.info("用户{}请求认证", userAccount);
        // 依据用户账号查找用户
        ApiResponse apiResponse = userService.selectByUserAccount(userAccount);
        // 账号不存在
        if (ApiResponse.FAIL_TYPE.equals(apiResponse.getType())) {
            throw new UnknownAccountException();
        }
        List<User> userList = (List<User>) apiResponse.getData();
        User user = userList.get(0);
        // 获取用户状态
        int userStatus = user.getUserStatus();
        // 获取用户密码
        String userPassword = user.getUserPassword();
        // 账号锁定
        if (userStatus == UserStatusEnum.LOCKED_ACCOUNT.getCode()) {
            throw new LockedAccountException();
        }
        // 账号禁用
        if (userStatus == UserStatusEnum.DISABLED_ACCOUNT.getCode()) {
            throw new DisabledAccountException();
        }
        // 盐
        String salt = user.getId();
        // 保存当前用户信息到shiro session中
        ShiroUtil.setCurrentUser(user);
        // 与UsernamePasswordToken(userAccount, userPassword)进行比较
        // 如果没有配置Shiro加密,会直接进行比较
        // 如果配置了Shiro的加密,会先对UsernamePasswordToken(userAccount, userPassword)中的密码进行加密,
        // 再和SimpleAuthenticationInfo(userAccount, userPassword, ByteSource.Util.bytes(salt), this.getName())中的密码进行比较
        return new SimpleAuthenticationInfo(userAccount, userPassword, ByteSource.Util.bytes(salt), this.getName());
    }

    /**
     * @description: 用于授权
     * @author cheng
     * @dateTime 2018/4/18 15:42
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        // 获取用户账号
        // String userAccount = (String) principals.getPrimaryPrincipal();
        // 依据用户账号在数据库中查找权限信息

        log.info("用户请求授权");

        // 角色
        List<String> roles = new ArrayList<>();
        roles.add("admin");
        roles.add("user");
        // 权限
        List<String> permissions = new ArrayList<>();
        permissions.add("admin:select");
        permissions.add("admin:delete");

        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        simpleAuthorizationInfo.addStringPermissions(permissions);
        simpleAuthorizationInfo.addRoles(roles);
        return simpleAuthorizationInfo;
    }

}

在ShiroConfig 中添加配置

    /**
     * @description:自定义realm
     * @author cheng
     * @dateTime 2018/4/18 15:44
     */
    @Bean
    public MyRealm createMyRealm() {
        MyRealm myRealm = new MyRealm();
        log.info("自定义realm");
        return myRealm;
    }

自定义加密

保存用户时
用户密码 -> 加密 -> 保存到数据库
加密方法

    /**
     * 散列次数
     */
    public static final int HASH_ITERATIONS = 2;

    /**
     * @description: 加密密码
     * @author cheng
     * @dateTime 2018/4/23 15:01
     */
    public static String encrypt(String password, String salt) {
        Md5Hash md5Hash = new Md5Hash(password, salt, HASH_ITERATIONS);
        return md5Hash.toString();
    }

用户认证时
用户密码 -> 加密 -> 认证
修改ShiroConfig中自定义Realm配置

    /**
     * @description:自定义realm
     * @author cheng
     * @dateTime 2018/4/18 15:44
     */
    @Bean
    public MyRealm createMyRealm() {
        // 加密相关
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        // 散列算法
        hashedCredentialsMatcher.setHashAlgorithmName(ShiroUtil.HASH_ALGORITHM_NAME);
        // 散列次数
        hashedCredentialsMatcher.setHashIterations(ShiroUtil.HASH_ITERATIONS);
        MyRealm myRealm = new MyRealm();
        myRealm.setCredentialsMatcher(hashedCredentialsMatcher);
        log.info("自定义realm");
        return myRealm;
    }

自定义缓存管理

自定义Cache

package com.ahut.shiro;

import com.ahut.utils.RedisUtil;
import com.ahut.utils.ShiroUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import org.springframework.util.SerializationUtils;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;

/**
 * @author cheng
 * @className: RedisShiroCache
 * @description: redis管理shiro缓存
 * @dateTime 2018/4/24 16:04
 */
@Slf4j
public class RedisShiroCache<K, V> implements Cache<K, V> {

    /**
     * @description: 获取加工后的key的字节数组
     * @author cheng
     * @dateTime 2018/4/24 9:57
     */
    private byte[] getKey(Object key) {
        return (ShiroUtil.SHIRO_CACHE_PREFIX + key).getBytes();
    }

    /**
     * @description: 从缓存中获取数据
     * @author cheng
     * @dateTime 2018/4/24 16:09
     */
    @Override
    public Object get(Object key) throws CacheException {
        if (key == null) {
            return null;
        }
        // 序列化键
        byte[] keyByteArray = getKey(key);
        // 从redis中获取数据
        byte[] valueByteArray = RedisUtil.get(keyByteArray);
        log.info("从缓存中获取数据");
        // 返回对应的数据
        return valueByteArray == null ? null : SerializationUtils.deserialize(valueByteArray);
    }

    /**
     * @description: 保存shiro缓存到redis
     * @author cheng
     * @dateTime 2018/4/24 16:13
     */
    @Override
    public Object put(Object key, Object value) throws CacheException {
        if (key == null || value == null) {
            return null;
        }
        // 序列化
        byte[] keyByteArray = getKey(key);
        byte[] valueByteArray = SerializationUtils.serialize((Serializable) value);
        RedisUtil.set(keyByteArray, valueByteArray);
        log.info("保存shiro缓存到redis");
        // 返回保存的值
        return SerializationUtils.deserialize(valueByteArray);
    }

    /**
     * @description: 从redis中删除
     * @author cheng
     * @dateTime 2018/4/24 16:19
     */
    @Override
    public Object remove(Object key) throws CacheException {
        if (key == null) {
            return null;
        }
        // 序列化
        byte[] keyByteArray = getKey(key);
        byte[] valueByteArray = RedisUtil.get(keyByteArray);
        // 删除
        RedisUtil.del(keyByteArray);
        log.info("从redis中删除");
        // 返回删除的数据
        return SerializationUtils.deserialize(valueByteArray);
    }

    /**
     * @description: 清空所有的缓存
     * @author cheng
     * @dateTime 2018/4/24 16:25
     */
    @Override
    public void clear() throws CacheException {
        log.info("清空所有的缓存");
        RedisUtil.delByPrefix(ShiroUtil.SHIRO_CACHE_PREFIX);
    }

    /**
     * @description: 缓存个数
     * @author cheng
     * @dateTime 2018/4/24 16:56
     */
    @Override
    public int size() {
        Set<byte[]> keyByteArraySet = RedisUtil.keys(ShiroUtil.SHIRO_CACHE_PREFIX);
        log.info("获取缓存个数");
        return keyByteArraySet.size();
    }

    /**
     * @description: 获取所有的key
     * @author cheng
     * @dateTime 2018/4/24 16:59
     */
    @Override
    public Set keys() {
        Set<byte[]> keyByteArraySet = RedisUtil.keys(ShiroUtil.SHIRO_CACHE_PREFIX);
        log.info("获取缓存所有的key");
        return keyByteArraySet;
    }

    /**
     * @description: 获取所有的value
     * @author cheng
     * @dateTime 2018/4/24 16:59
     */
    @Override
    public Collection values() {
        Set keySet = this.keys();
        List<Object> valueList = new ArrayList<>(16);
        for (Object key : keySet) {
            byte[] keyByteArray = SerializationUtils.serialize(key);
            byte[] valueByteArray = RedisUtil.get(keyByteArray);
            valueList.add(SerializationUtils.deserialize(valueByteArray));
        }
        log.info("获取缓存所有的value");
        return valueList;
    }

}

自定义CacheManager

package com.ahut.shiro;

import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import org.apache.shiro.cache.CacheManager;

/**
 * @author cheng
 * @className: RedisShiroCacheManager
 * @description: redis shiro 缓存管理器
 * @dateTime 2018/4/24 15:58
 */
public class RedisShiroCacheManager implements CacheManager {

    /**
     * @description:
     * @author cheng
     * @dateTime 2018/4/24 16:05
     */
    @Override
    public <K, V> Cache<K, V> getCache(String name) throws CacheException {
        return new RedisShiroCache<K, V>();
    }
}

在ShiroConfig中添加配置

    /**
     * @description: 自定义缓存管理器
     * @author cheng
     * @dateTime 2018/4/24 15:59
     */
    public RedisShiroCacheManager createCacheManager() {
        RedisShiroCacheManager redisShiroCacheManager = new RedisShiroCacheManager();
        log.info("自定义CacheManager");
        return redisShiroCacheManager;
    }

自定义会话管理

自定义sessionDao

扫描二维码关注公众号,回复: 1469879 查看本文章
package com.ahut.shiro;

import com.ahut.utils.RedisUtil;
import com.ahut.utils.ShiroUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO;
import org.springframework.util.SerializationUtils;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;

/**
 * @author cheng
 * @className: RedisShiroSessionDao
 * @description: 使用redis管理shiro session
 * @dateTime 2018/4/24 9:26
 */
@Slf4j
public class RedisShiroSessionDao extends EnterpriseCacheSessionDAO {

    /**
     * @description: 获取加工后的key的字节数组
     * @author cheng
     * @dateTime 2018/4/24 9:57
     */
    private byte[] getKey(String key) {
        return (ShiroUtil.SHIRO_SESSION_PREFIX + key).getBytes();
    }

    /**
     * @description: 更新会话;如更新会话最后访问时间/停止会话/设置超时时间/设置移除属性等会调用
     * @author cheng
     * @dateTime 2018/4/24 9:32
     */
    @Override
    public void doUpdate(Session session) {
        // 判断session
        if (session != null && session.getId() != null) {
            byte[] key = getKey(session.getId().toString());
            // 序列化session
            byte[] value = SerializationUtils.serialize(session);
            // 把session信息存储到redis中
            RedisUtil.set(key, value);
            log.info("更新session:{}", session);
        }
    }

    /**
     * @description: 删除会话;当会话过期/会话停止(如用户退出时)会调用
     * @author cheng
     * @dateTime 2018/4/24 9:31
     */
    @Override
    protected void doDelete(Session session) {
        // 判断session
        if (session != null && session.getId() != null) {
            byte[] key = getKey(session.getId().toString());
            // 从redis中删除session
            RedisUtil.del(key);
            log.info("删除session:{}", session);
        }
    }

    /**
     * @description: 如DefaultSessionManager在创建完session后会调用该方法;
     * 如保存到关系数据库/文件系统/NoSQL数据库;即可以实现会话的持久化;
     * 返回会话ID;主要此处返回的ID.equals(session.getId());
     * @author cheng
     * @dateTime 2018/4/24 9:32
     */
    @Override
    public Serializable doCreate(Session session) {
        // 判断session
        if (session != null) {
            // 获取sessionId
            Serializable sessionId = super.doCreate(session);
            byte[] key = getKey(sessionId.toString());
            // 序列化session
            byte[] value = SerializationUtils.serialize(session);
            // 把session信息存储到redis中
            RedisUtil.set(key, value);
            // 设置过期时间
            RedisUtil.expire(key, ShiroUtil.EXPIRE_SECONDS);
            log.info("创建session:{}", session);
            return sessionId;
        }
        return null;
    }

    /**
     * @description: 根据会话ID获取会话
     * @author cheng
     * @dateTime 2018/4/24 9:32
     */
    @Override
    public Session doReadSession(Serializable sessionId) {
        if (sessionId != null) {
            byte[] key = getKey(sessionId.toString());
            byte[] value = RedisUtil.get(key);
            // 反序列化session
            Session session = (Session) SerializationUtils.deserialize(value);
            log.info("获取session:{}", session);
            return session;
        }
        return null;
    }

    /**
     * @description: 获取当前所有活跃用户,如果用户量多此方法影响性能
     * @author cheng
     * @dateTime 2018/4/24 9:32
     */
    @Override
    public Collection<Session> getActiveSessions() {
        List<Session> sessionList = new ArrayList<>(16);
        // 从redis从查询
        Set<byte[]> keyByteArraySet = RedisUtil.keys(ShiroUtil.SHIRO_SESSION_PREFIX);
        for (byte[] keyByteArray : keyByteArraySet) {
            // 反序列化
            Session session = (Session) SerializationUtils.deserialize(keyByteArray);
            sessionList.add(session);
        }
        return sessionList;
    }

}

在ShiroConfig中配置SessionDao

    /**
     * @description: 自定义sessionDao
     * @author cheng
     * @dateTime 2018/4/24 10:47
     */
    public RedisShiroSessionDao createRedisShiroSessionDao() {
        RedisShiroSessionDao sessionDao = new RedisShiroSessionDao();
        // 设置缓存管理器
        sessionDao.setCacheManager(createCacheManager());
        log.info("自定义sessionDao");
        return sessionDao;
    }

在ShiroConfig中自定义Shiro session cookie信息

    /**
     * @description: 自定义shiro session cookie
     * @author cheng
     * @dateTime 2018/4/24 11:09
     */
    public SimpleCookie createSessionIdCookie() {
        SimpleCookie simpleCookie = new SimpleCookie(ShiroUtil.SESSIONID_COOKIE_NAME);
        // 保证该系统不会受到跨域的脚本操作攻击
        simpleCookie.setHttpOnly(true);
        // 定义Cookie的过期时间,单位为秒,如果设置为-1表示浏览器关闭,则Cookie消失
        simpleCookie.setMaxAge(-1);
        log.info("自定义SessionIdCookie");
        return simpleCookie;
    }

在ShiroConfig中配置SessionManager

    /**
     * @description: 自定义sessionManager
     * @author cheng
     * @dateTime 2018/4/24 10:37
     */
    public SessionManager createMySessionManager() {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        // 自定义sessionDao
        sessionManager.setSessionDAO(createRedisShiroSessionDao());
        // session的失效时长,单位是毫秒
        sessionManager.setGlobalSessionTimeout(ShiroUtil.GLOBAL_SESSION_TIMEOUT);
        // 删除失效的session
        sessionManager.setDeleteInvalidSessions(true);
        // 所有的session一定要将id设置到Cookie之中,需要提供有Cookie的操作模版
        sessionManager.setSessionIdCookie(createSessionIdCookie());
        // 定义sessionIdCookie模版可以进行操作的启用
        sessionManager.setSessionIdCookieEnabled(true);
        log.info("配置sessionManager");
        return sessionManager;
    }

自定义记住我

在ShiroConfig中自定义记住我cookie

    /**
     * @description: 记住我cookie
     * @author cheng
     * @dateTime 2018/4/24 15:39
     */
    public SimpleCookie createRemeberMeCookie() {
        SimpleCookie simpleCookie = new SimpleCookie(ShiroUtil.REMEBER_ME_COOKIE_NAME);
        // 保证该系统不会受到跨域的脚本操作攻击
        simpleCookie.setHttpOnly(true);
        // 定义Cookie的过期时间,单位为秒,如果设置为-1表示浏览器关闭,则Cookie消失
        simpleCookie.setMaxAge(2592000);
        log.info("自定义RemeberMeCookie");
        return simpleCookie;
    }

在ShiroConfig中配置RememberMeManager

    /**
     * @description: 自定义记住我
     * @author cheng
     * @dateTime 2018/4/24 15:35
     */
    public CookieRememberMeManager createRememberMeManager() {
        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
        // 设置记住我的cookie
        cookieRememberMeManager.setCookie(createRemeberMeCookie());
        log.info("配置RemeberMeManager");
        return cookieRememberMeManager;
    }

登录时,使用记住我

package com.ahut.serviceImpl;

import com.ahut.common.ApiResponse;
import com.ahut.entity.User;
import com.ahut.service.LoginService;
import com.ahut.service.UserService;
import com.ahut.utils.ShiroUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;

/**
 * @author cheng
 * @className: LoginServiceImpl
 * @description:
 * @dateTime 2018/4/19 9:21
 */
@Service
@Transactional
@Slf4j
public class LoginServiceImpl implements LoginService {

    /**
     * 用户业务逻辑
     */
    @Autowired
    private UserService userService;

    /**
     * @description: 登录
     * @author cheng
     * @dateTime 2018/4/19 9:21
     */
    @Override
    public ApiResponse login(String userAccount, String userPassword) {
        // 获取当前用户
        Subject subject = SecurityUtils.getSubject();
        // 自己创建令牌
        UsernamePasswordToken token = new UsernamePasswordToken(userAccount, userPassword);
        // 当前登录用户信息
        User user = null;
        // 提示信息
        String msg = null;
        try {
            // 记住我
            token.setRememberMe(true);
            subject.login(token);
            // 用户认证成功,获取当前用户
            user = ShiroUtil.getCurrentUser();
            // 完成认证的后续操作
            // 修改用户信息
            user.setLastLoginTime(new Date());
            userService.updateUser(user);
            msg = "用户登录成功";
            return ApiResponse.createSuccessResponse(msg, user);
        } catch (UnknownAccountException e) {
            msg = "用户账号不存在";
            log.warn(msg, e);
        } catch (LockedAccountException e) {
            msg = "用户账号被锁定";
            log.warn(msg, e);
        } catch (DisabledAccountException e) {
            msg = "用户账号被禁用";
            log.warn(msg, e);
        } catch (IncorrectCredentialsException e) {
            msg = "用户密码错误";
            log.warn(msg, e);
        }
        // 用户认证失败,删除当前用户
        ShiroUtil.removeCurrentUser();
        return ApiResponse.createFailResponse(msg);
    }

}

完整的ShiroConfig配置

package com.ahut.config;

import com.ahut.shiro.MyRealm;
import com.ahut.shiro.RedisShiroCacheManager;
import com.ahut.shiro.RedisShiroSessionDao;
import com.ahut.utils.ShiroUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;

/**
 * @author cheng
 * @className: ShiroConfig
 * @description: shiro配置
 * @dateTime 2018/4/18 15:38
 */
@Configuration
@Slf4j
public class ShiroConfig {

    /**
     * @description:自定义realm
     * @author cheng
     * @dateTime 2018/4/18 15:44
     */
    @Bean
    public MyRealm createMyRealm() {
        // 加密相关
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        // 散列算法
        hashedCredentialsMatcher.setHashAlgorithmName(ShiroUtil.HASH_ALGORITHM_NAME);
        // 散列次数
        hashedCredentialsMatcher.setHashIterations(ShiroUtil.HASH_ITERATIONS);
        MyRealm myRealm = new MyRealm();
        myRealm.setCredentialsMatcher(hashedCredentialsMatcher);
        log.info("自定义realm");
        return myRealm;
    }

    /**
     * @description: 自定义sessionDao
     * @author cheng
     * @dateTime 2018/4/24 10:47
     */
    public RedisShiroSessionDao createRedisShiroSessionDao() {
        RedisShiroSessionDao sessionDao = new RedisShiroSessionDao();
        // 设置缓存管理器
        sessionDao.setCacheManager(createCacheManager());
        log.info("自定义sessionDao");
        return sessionDao;
    }

    /**
     * @description: 自定义shiro session cookie
     * @author cheng
     * @dateTime 2018/4/24 11:09
     */
    public SimpleCookie createSessionIdCookie() {
        SimpleCookie simpleCookie = new SimpleCookie(ShiroUtil.SESSIONID_COOKIE_NAME);
        // 保证该系统不会受到跨域的脚本操作攻击
        simpleCookie.setHttpOnly(true);
        // 定义Cookie的过期时间,单位为秒,如果设置为-1表示浏览器关闭,则Cookie消失
        simpleCookie.setMaxAge(-1);
        log.info("自定义SessionIdCookie");
        return simpleCookie;
    }


    /**
     * @description: 自定义sessionManager
     * @author cheng
     * @dateTime 2018/4/24 10:37
     */
    public SessionManager createMySessionManager() {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        // 自定义sessionDao
        sessionManager.setSessionDAO(createRedisShiroSessionDao());
        // session的失效时长,单位是毫秒
        sessionManager.setGlobalSessionTimeout(ShiroUtil.GLOBAL_SESSION_TIMEOUT);
        // 删除失效的session
        sessionManager.setDeleteInvalidSessions(true);
        // 所有的session一定要将id设置到Cookie之中,需要提供有Cookie的操作模版
        sessionManager.setSessionIdCookie(createSessionIdCookie());
        // 定义sessionIdCookie模版可以进行操作的启用
        sessionManager.setSessionIdCookieEnabled(true);
        log.info("配置sessionManager");
        return sessionManager;
    }

    /**
     * @description: 记住我cookie
     * @author cheng
     * @dateTime 2018/4/24 15:39
     */
    public SimpleCookie createRemeberMeCookie() {
        SimpleCookie simpleCookie = new SimpleCookie(ShiroUtil.REMEBER_ME_COOKIE_NAME);
        // 保证该系统不会受到跨域的脚本操作攻击
        simpleCookie.setHttpOnly(true);
        // 定义Cookie的过期时间,单位为秒,如果设置为-1表示浏览器关闭,则Cookie消失
        simpleCookie.setMaxAge(2592000);
        log.info("自定义RemeberMeCookie");
        return simpleCookie;
    }

    /**
     * @description: 自定义记住我
     * @author cheng
     * @dateTime 2018/4/24 15:35
     */
    public CookieRememberMeManager createRememberMeManager() {
        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
        // 设置记住我的cookie
        cookieRememberMeManager.setCookie(createRemeberMeCookie());
        log.info("配置RemeberMeManager");
        return cookieRememberMeManager;
    }

    /**
     * @description: 自定义缓存管理器
     * @author cheng
     * @dateTime 2018/4/24 15:59
     */
    public RedisShiroCacheManager createCacheManager() {
        RedisShiroCacheManager redisShiroCacheManager = new RedisShiroCacheManager();
        log.info("自定义CacheManager");
        return redisShiroCacheManager;
    }

    /**
     * @description: 注意方法返回值SecurityManager为org.apache.shiro.mgt.SecurityManager, 不要导错包
     * @author cheng
     * @dateTime 2018/4/18 15:48
     */
    public SecurityManager createSecurityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 自定义realm
        securityManager.setRealm(createMyRealm());
        // 自定义sessionManager
        securityManager.setSessionManager(createMySessionManager());
        // 自定义rememberMeManager
        securityManager.setRememberMeManager(createRememberMeManager());
        // 自定义cacheManager
        securityManager.setCacheManager(createCacheManager());
        log.info("配置rsecurityManager");
        return securityManager;
    }

    /**
     * @description: shiro web过滤器
     * @author cheng
     * @dateTime 2018/4/18 15:50
     */
    @Bean
    public ShiroFilterFactoryBean createShiroFilter() {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(createSecurityManager());
        // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
        shiroFilterFactoryBean.setLoginUrl("/v1/toLoginPage");

        // 过滤器
        Map<String, String> filterChainDefinitionMap = new HashMap<>();
        // 配置不会被过滤的链接 顺序判断
        // 过虑器链定义,从上向下顺序执行,一般将/**放在最下边
        // 用户注册匿名访问
        filterChainDefinitionMap.put("/v1/users/", "anon");
        // 管理员登录页面
        filterChainDefinitionMap.put("/v1/toLoginPage", "anon");
        // 管理员登录
        filterChainDefinitionMap.put("/v1/login", "anon");
        // 对静态资源设置匿名访问
        // anon:所有url都都可以匿名访问
        filterChainDefinitionMap.put("/static/**", "anon");

        // authc:所有url都必须认证通过才可以访问
        filterChainDefinitionMap.put("/**", "anon");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

}

猜你喜欢

转载自blog.csdn.net/qq_28988969/article/details/80076689