shiro登陆、共享session、权限,redisTemplate
**************************************** 登陆拦截 ****************************************
登陆过后才不被拦截
@Bean
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) throws IOException {
ShiroFilterFactoryBean factory = new ShiroFilterFactoryBean();
factory.setSecurityManager(securityManager);
factory.setLoginUrl("/unauthorizedxmh");
factory.setUnauthorizedUrl("/forbidden");
Map<String, String> filterMap = new LinkedHashMap();
// OrderedProperties properties = new OrderedProperties("classpath:shiro.properties");
// filterMap.putAll(Maps.fromProperties(properties));
filterMap.put("/login","anon");
// filterMap.put("/**","authc,user");
filterMap.put("/**","authc");
factory.setFilterChainDefinitionMap(filterMap);
Map<String, Filter> filters = factory.getFilters();
filters.put("authc",new UserLoginFilter());
return factory;
}
/**
* 自定义shiro登录过滤器,判断是ajax请求返回json,否则重定向页面
*/
public class UserLoginFilter extends FormAuthenticationFilter {
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
if(!isAjaxRequest(WebUtils.toHttp(request))){
return super.onAccessDenied(request, response);
}else{
HttpServletResponse httpServletResponse = WebUtils.toHttp(response);
//这里是个坑,如果不设置的接受的访问源,那么前端都会报跨域错误,因为这里还没到corsConfig里面
httpServletResponse.setHeader("Access-Control-Allow-Origin", ((HttpServletRequest) request).getHeader("Origin"));
httpServletResponse.setHeader("Access-Control-Allow-Credentials", "true");
httpServletResponse.setCharacterEncoding("UTF-8");
httpServletResponse.setContentType("application/json");
JSONObject object = new JSONObject();
object.put("code",400);
object.put("msg","登录超时,请重新登录");
object.put("timestamp",System.currentTimeMillis());
PrintWriter writer = httpServletResponse.getWriter();
writer.println(object.toJSONString());
writer.flush();
writer.close();
//todo
return false;
}
}
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
if (request instanceof HttpServletRequest) {
if ("OPTIONS".equals(((HttpServletRequest) request).getMethod().toUpperCase())) {
return true;
}
}
return super.isAccessAllowed(request, response, mappedValue);
}
/**
* 是否是Ajax请求
*
* @param request
* @return
*/
private static boolean isAjaxRequest(HttpServletRequest request) {
String requestedWith = request.getHeader("x-requested-with");
if (requestedWith != null && "XMLHttpRequest".equalsIgnoreCase(requestedWith)) {
return true;
} else {
return false;
}
}
}
**************************************** 权限拦截 ****************************************
无权访问报异常 org.apache.shiro.authz.UnauthorizedException
第一种写法
@RequestMapping("/hello1")
@ResponseBody
@RequiresPermissions("xmh.test.permission")//必须先login
public String hello1() {
System.out.println("***************hello1");
return "hello111:";
}
第二种写法
boolean b1 = SecurityUtils.getSubject().isPermitted("xxxx");
boolean b2 = SecurityUtils.getSubject().isPermitted("xmh.test.permission");
System.out.println(b1+" "+b2);
public class AuthorizeRealm extends AuthorizingRealm {
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
principals.getPrimaryPrincipal();
// 添加用户权限
info.addStringPermission("xmh.test.permission");
System.out.println("AuthorizationInfo:!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
//https://blog.csdn.net/wwwffy/article/details/78188973
return info;
}
/**
* 开启shiro aop注解支持,使用代理方式; 所以需要开启代码支持; Controller才能使用@RequiresPermissions
*
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
@Bean
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
@Bean
@DependsOn("lifecycleBeanPostProcessor")
public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();
creator.setProxyTargetClass(true);
return creator;
}
扩展知识
@ControllerAdvice
public class AdviceController { 异常处理函数
UsernamePasswordToken authenticationToken = new UsernamePasswordToken();
authenticationToken.setUsername(userId);
authenticationToken.setPassword("001".toCharArray());
currentUser.login(authenticationToken);//登录时触发doGetAuthenticationInfo登录校验
@Bean
public AuthorizeRealm authorizeRealm(){
return new AuthorizeRealm();
}
@Bean
public DefaultWebSecurityManager securityManager(SessionManager sessionManager) {//AuthorizingRealm realm, SessionManager sessionManager
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
manager.setRealm(authorizeRealm());
// manager.setCacheManager(new RedisCacheManager());
manager.setSessionManager(sessionManager);
return manager;
}
public class AuthorizeRealm extends AuthorizingRealm {
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
principals.getPrimaryPrincipal();
// 添加用户权限
info.addStringPermission("user");
System.out.println("AuthorizationInfo:****************");
//https://blog.csdn.net/wwwffy/article/details/78188973
return info;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken)token;
String userName = usernamePasswordToken.getUsername();
System.out.println("AuthorizeRealm****************userName:"+userName);
StringBuilder sb = new StringBuilder(100);
for (int i = 0; i < usernamePasswordToken.getPassword().length; i++) {
sb.append(usernamePasswordToken.getPassword()[i]);
}
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(userName, sb.toString(), getName());
return simpleAuthenticationInfo;
// return null;//登录失败
}
}
redis哨兵
分布式集群,共享session
http://localhost:9990/hello3?userId=1qaz21111
http://localhost:9990/hello1 获取相同userId
http://localhost:9999/hello1 获取相同userId
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.1</version>
</dependency>
@Autowired
private RedisTemplate<Serializable, Serializable> redisTemplate;
@RequestMapping("/hello3")
@ResponseBody
public ResultVo hello3(String userId) {
ResultVo resultVo = new ResultVo();
System.out.println("TestController2*****************************:userId:"+userId);
Subject currentUser = SecurityUtils.getSubject();
Session session = currentUser.getSession();
session.setAttribute("userId",userId);
System.out.println("sessionId:"+session.getId());
resultVo.setMessage((String)session.getId());
return resultVo;
}
@RequestMapping("/hello1")
@ResponseBody
public String hello1() {
System.out.println("hello1");
Subject currentUser = SecurityUtils.getSubject();
Session session = currentUser.getSession();
String userId = (String) session.getAttribute("userId");
System.out.println("sessionId:"+session.getId()+",userId:"+userId);
return "hello:"+session.getId()+","+userId;
}
1,在创建Subject的时候还没有用户信息,只有当subject.login(token)的时候会获取用户信息,才能将用户信息放入session中
2,Shiro对于ThreadLocal<T>的使用方式是将其封装为对自定义的ThreadContext的调用。关于ThreadContext,其显式绑定的就两个实例:Subject和SecurityManager 。这个观察其定义的bind方法就知晓了
3, 但若是容器不存在session,那么Shiro会提供内置的企业级session来管理。当然在开发中,也可以使⽤SessionDAO允许
数据源持久化Session。
@Configuration
//@ConditionalOnBean(Realm.class)
public class ShiroConfig {
@Bean
public Cookie cookie() {
SimpleCookie cookie = new SimpleCookie("OTC-SESSIONID");
cookie.setSecure(false);
cookie.setHttpOnly(true);
cookie.setPath("/");
cookie.setMaxAge(86400);
// cookie.setMaxAge(-1);
return cookie;
}
@Bean
public SessionListener sessionListener() {
return new SessionListener();
}
@Bean
public SessionDAO sessionDao() {//Realm realm
RedisSessionDAO dao = new RedisSessionDAO();
// realm.setSessionDAO(dao);//其他模块使用
return dao;
}
@Bean
public SessionManager sessionManager(SessionDAO sessionDao, SessionListener sessionListener, Cookie cookie) {//
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
sessionManager.setGlobalSessionTimeout(86400000L);
sessionManager.setSessionDAO(sessionDao);
sessionManager.getSessionListeners().add(sessionListener);
sessionManager.setSessionIdCookie(cookie);
return sessionManager;
}
@Bean
public DefaultWebSecurityManager securityManager(SessionManager sessionManager) {//AuthorizingRealm realm, SessionManager sessionManager
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
// manager.setRealm(realm);
// manager.setCacheManager(new RedisCacheManager());
manager.setSessionManager(sessionManager);
return manager;
}
@Bean
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) throws IOException {
ShiroFilterFactoryBean factory = new ShiroFilterFactoryBean();
factory.setSecurityManager(securityManager);
// factory.setLoginUrl("/unauthorized");
// factory.setUnauthorizedUrl("/forbidden");
// Map<String, String> filterMap = InstanceUtil.newLinkedHashMap();
// OrderedProperties properties = new OrderedProperties("classpath:config/shiro.properties");
// filterMap.putAll(Maps.fromProperties(properties));
// factory.setFilterChainDefinitionMap(filterMap);
// Map<String, Filter> filters = factory.getFilters();
// filters.put("authc",new UserLoginFilter());
return factory;
}
}
public class RedisSessionDAO extends AbstractSessionDAO {
private static final int EXPIRE_TIME = 600;
@Autowired
private RedisTemplate<Serializable, Serializable> redisTemplate;
private final static Logger logger = LogManager.getLogger();
public void delete(Serializable sessionId) {
if (sessionId != null) {
byte[] sessionKey = buildRedisSessionKey(sessionId);
RedisConnectionFactory factory = redisTemplate.getConnectionFactory();
RedisConnection conn = null;
try {
conn = RedisConnectionUtils.getConnection(factory);
conn.del(sessionKey);
} finally {
RedisConnectionUtils.releaseConnection(conn, factory);
}
}
}
@Override
protected Serializable doCreate(Session session) {
Serializable sessionId = generateSessionId(session);
assignSessionId(session, sessionId);
saveSession(session);
return sessionId;
}
@Override
protected Session doReadSession(Serializable sessionId) {
byte[] sessionKey = buildRedisSessionKey(sessionId);
RedisConnectionFactory factory = redisTemplate.getConnectionFactory();
RedisConnection conn = null;
try {
conn = RedisConnectionUtils.getConnection(factory);
byte[] value = conn.get(sessionKey);
if (value == null) {
return null;
}
Session session = SerializeUtil.deserialize(value, SimpleSession.class);
return session;
} finally {
RedisConnectionUtils.releaseConnection(conn, factory);
}
}
@Override
public void update(Session session) throws UnknownSessionException {
saveSession(session);
}
@Override
public void delete(Session session) {
if (session != null) {
Serializable id = session.getId();
if (id != null) {
RedisConnectionFactory factory = redisTemplate.getConnectionFactory();
RedisConnection conn = null;
try {
conn = RedisConnectionUtils.getConnection(factory);
conn.del(buildRedisSessionKey(id));
} finally {
RedisConnectionUtils.releaseConnection(conn, factory);
}
}
}
}
@Override
public Collection<Session> getActiveSessions() {
// List<Session> list = InstanceUtil.newArrayList();
List<Session> list = new ArrayList<>();
RedisConnectionFactory factory = redisTemplate.getConnectionFactory();
RedisConnection conn = null;
try {
conn = RedisConnectionUtils.getConnection(factory);
Set<byte[]> set = conn.keys((Constants.REDIS_SHIRO_SESSION + "*").getBytes());
for (byte[] key : set) {
list.add(SerializeUtil.deserialize(conn.get(key), SimpleSession.class));
}
} finally {
RedisConnectionUtils.releaseConnection(conn, factory);
}
return list;
}
private void saveSession(Session session) {
if (session == null || session.getId() == null) {
throw new UnknownSessionException("session is empty");
}
byte[] sessionKey = buildRedisSessionKey(session.getId());
// int sessionTimeOut = PropertiesUtil.getInt("session.maxInactiveInterval", getExpireTime());
int sessionTimeOut =86400;
byte[] value = SerializeUtil.serialize(session);
RedisConnectionFactory factory = redisTemplate.getConnectionFactory();
RedisConnection conn = null;
try {
conn = RedisConnectionUtils.getConnection(factory);
conn.set(sessionKey, value, Expiration.seconds(sessionTimeOut), SetOption.UPSERT);
} finally {
RedisConnectionUtils.releaseConnection(conn, factory);
}
}
protected int getExpireTime() {
return EXPIRE_TIME;
}
private byte[] buildRedisSessionKey(Serializable sessionId) {
return (Constants.REDIS_SHIRO_SESSION + sessionId).getBytes();
}
}
public class SessionListener implements org.apache.shiro.session.SessionListener {
private Logger logger = LogManager.getLogger();
@Autowired
RedisTemplate redisTemplate;
/* (non-Javadoc)
* @see org.apache.shiro.session.SessionListener#onStart(org.apache.shiro.session.Session) */
@Override
public void onStart(Session session) {
session.setAttribute(Constants.WEBTHEME, "default");
logger.info("创建了一个Session连接:[" + session.getId() + "]");
redisTemplate.opsForSet().add(Constants.ALLUSER_NUMBER, session.getId());
}
/* (non-Javadoc)
* @see org.apache.shiro.session.SessionListener#onStop(org.apache.shiro.session.Session) */
@Override
public void onStop(Session session) {
if (getAllUserNumber() > 0) {
logger.info("销毁了一个Session连接:[" + session.getId() + "]");
}
session.removeAttribute(Constants.CURRENT_USER);
redisTemplate.opsForSet().remove(Constants.ALLUSER_NUMBER, session.getId());
}
/* (non-Javadoc)
* @see org.apache.shiro.session.SessionListener#onExpiration(org.apache.shiro.session.Session) */
@Override
public void onExpiration(Session session) {
onStop(session);
}
/** 获取在线用户数量 */
public Integer getAllUserNumber() {
return redisTemplate.opsForSet().size(Constants.ALLUSER_NUMBER).intValue();
}
}
@Configuration
public class RedisConfig {
// @Bean
// public GenericObjectPoolConfig redisPoolConfig() {
// GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
// poolConfig.setMinIdle(PropertiesUtil.getInt("redis.minIdle"));
// poolConfig.setMaxIdle(PropertiesUtil.getInt("redis.maxIdle"));
// poolConfig.setMaxTotal(PropertiesUtil.getInt("redis.maxTotal"));
// poolConfig.setMaxWaitMillis(PropertiesUtil.getInt("redis.maxWaitMillis"));
// return poolConfig;
// }
//
// @Bean(destroyMethod = "shutdown")
// @ConditionalOnMissingBean(ClientResources.class)
// public ClientResources clientResources() {
// return DefaultClientResources.create();
// }
/**
* 连接redis的工厂类
*
* @return
*/
@Bean(name = "redisConnectionFactory")
public RedisConnectionFactory redisConnectionFactory() {//GenericObjectPoolConfig redisPoolConfig, ClientResources clientResources
LettuceConnectionFactory connectionFactory;
// String nodes = PropertiesUtil.getString("spring.redis.cluster.nodes");
// String master = PropertiesUtil.getString("redis.master");
// String sentinels = PropertiesUtil.getString("redis.sentinels");
// Duration commandTimeout = Duration.ofMillis(PropertiesUtil.getInt("redis.commandTimeout", 60000));
// Duration shutdownTimeout = Duration.ofMillis(PropertiesUtil.getInt("redis.shutdownTimeout", 5000));
// LettucePoolingClientConfiguration.LettucePoolingClientConfigurationBuilder builder = LettucePoolingClientConfiguration.builder()
// .poolConfig(redisPoolConfig).commandTimeout(commandTimeout).shutdownTimeout(shutdownTimeout)
// .clientResources(clientResources);
// LettuceClientConfiguration clientConfiguration = builder.build();
RedisPassword password = RedisPassword.of("123");
// String host = PropertiesUtil.getString("redis.host", "localhost");
// Integer port = PropertiesUtil.getInt("redis.port", 6379);
// Integer database = PropertiesUtil.getInt("redis.database", 0);
// if (DataUtil.isNotEmpty(nodes)) {
// List<String> list = InstanceUtil.newArrayList(nodes.split(","));
// RedisClusterConfiguration configuration = new RedisClusterConfiguration(list);
// configuration.setMaxRedirects(PropertiesUtil.getInt("redis.cluster.max-redirects"));
// configuration.setPassword(password);
// connectionFactory = new LettuceConnectionFactory(configuration, clientConfiguration);
// } else if (DataUtil.isNotEmpty(master) && DataUtil.isNotEmpty(sentinels)) {
// Set<String> set = InstanceUtil.newHashSet(sentinels.split(","));
// RedisSentinelConfiguration configuration = new RedisSentinelConfiguration(master, set);
// configuration.setPassword(password);
// connectionFactory = new LettuceConnectionFactory(configuration, clientConfiguration);
// } else {
RedisStandaloneConfiguration configuration = new RedisStandaloneConfiguration();
configuration.setPassword(password);
configuration.setHostName("192.168.89.128");
configuration.setPort(6379);
configuration.setDatabase(0);
connectionFactory = new LettuceConnectionFactory(configuration); AbstractRedisClient s;
// }
return connectionFactory;
}
@Bean(name = "redisTemplate")
public RedisTemplate<Serializable, Serializable> redisTemplate() {//<String, Object>
//StringRedisTemplate的构造方法中默认设置了stringSerializer
RedisTemplate<Serializable, Serializable> template = new RedisTemplate<>();
//设置开启事务
template.setEnableTransactionSupport(true);
//set key serializer
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// JdkSerializationRedisSerializer jdkSerializationRedisSerializer = new JdkSerializationRedisSerializer();
GenericFastJsonRedisSerializer valueSerializer = new GenericFastJsonRedisSerializer();
template.setKeySerializer(stringRedisSerializer);
template.setValueSerializer(valueSerializer);
template.setConnectionFactory(redisConnectionFactory());
template.afterPropertiesSet();
return template;
}
}
public final class SerializeUtil {
private SerializeUtil() {
}
private static final Logger logger = LogManager.getLogger();
/**
* 序列化
*
* @param object
* @return
*/
public static final byte[] serialize(Object object) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = null;
try {
oos = new ObjectOutputStream(baos);
oos.writeObject(object);
return baos.toByteArray();
} catch (IOException ex) {
throw new RuntimeException(ex.getMessage(), ex);
} finally {
try {
if (oos != null) {
oos.close();
}
} catch (Exception e) {
logger.error("", e);
}
try {
if (baos != null) {
baos.close();
}
} catch (Exception e) {
logger.error("", e);
}
}
}
/**
* 反序列化
*
* @param bytes
* @return
*/
public static final Object deserialize(byte[] bytes) {
return deserialize(bytes, Object.class);
}
/**
* 反序列化
*
* @param bytes
* @return
*/
@SuppressWarnings("unchecked")
public static final <K> K deserialize(byte[] bytes, Class<K> cls) {
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
ObjectInputStream ois = null;
try {
ois = new ObjectInputStream(bais);
return (K)ois.readObject();
} catch (IOException ex) {
throw new RuntimeException(ex.getMessage(), ex);
} catch (ClassNotFoundException ex) {
throw new RuntimeException(ex.getMessage(), ex);
} finally {
try {
if (ois != null) {
ois.close();
}
} catch (Exception e) {
logger.error("", e);
}
try {
if (bais != null) {
bais.close();
}
} catch (Exception e) {
logger.error("", e);
}
}
}
}

浙公网安备 33010602011771号