整合内容包括
<dependency>
<groupId>org.apache.shirogroupId>
<artifactId>shiro-springartifactId>
<version>1.3.2version>
dependency>
<dependency>
<groupId>redis.clientsgroupId>
<artifactId>jedisartifactId>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
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();
}
}
}
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 userList = (List) 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 roles = new ArrayList<>();
roles.add("admin");
roles.add("user");
// 权限
List 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 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 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
自定义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 Cache getCache(String name) throws CacheException {
return new RedisShiroCache();
}
}
在ShiroConfig中添加配置
/**
* @description: 自定义缓存管理器
* @author cheng
* @dateTime 2018/4/24 15:59
*/
public RedisShiroCacheManager createCacheManager() {
RedisShiroCacheManager redisShiroCacheManager = new RedisShiroCacheManager();
log.info("自定义CacheManager");
return redisShiroCacheManager;
}
自定义sessionDao
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 getActiveSessions() {
List 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);
}
}
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 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;
}
}