shiro登陆、权限,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;//登录失败 } }
分布式集群,共享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); } } } }