package com.gxzc.zen.umps.config import com.gxzc.zen.umps.constant.ZenHttpSession import com.gxzc.zen.umps.filter.AjaxAuthorizationFilter import com.gxzc.zen.umps.filter.UrlPermissionsFilter import com.gxzc.zen.umps.filter.ZenCorsAnonymousFilter import com.gxzc.zen.umps.filter.ZenCorsPathMatchingFilter import org.apache.shiro.authc.credential.HashedCredentialsMatcher import org.apache.shiro.spring.LifecycleBeanPostProcessor import org.apache.shiro.spring.web.ShiroFilterFactoryBean import org.apache.shiro.web.filter.authc.AnonymousFilter import org.apache.shiro.web.mgt.DefaultWebSecurityManager import org.apache.shiro.web.servlet.SimpleCookie import org.springframework.boot.context.properties.ConfigurationProperties import org.springframework.boot.web.servlet.FilterRegistrationBean import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import org.springframework.context.annotation.DependsOn import org.springframework.core.annotation.Order import org.springframework.data.redis.connection.jedis.JedisConnectionFactory import org.springframework.data.redis.core.RedisTemplate import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer import org.springframework.data.redis.serializer.StringRedisSerializer import org.springframework.web.filter.DelegatingFilterProxy import javax.servlet.DispatcherType import javax.servlet.Filter /** * Shiro配置 * @author NorthLan * @date 2018/4/21 * @url https://noahlan.com */ @Configuration class ShiroConfig { @Bean @ConfigurationProperties(prefix = "shiro.redis") fun shiroRedisProperties(): ShiroRedisProperties { return ShiroRedisProperties() } @Bean("shiroFilterRegistrationBean") @DependsOn("shiroFilter") fun filterRegistrationBean(): FilterRegistrationBean { return FilterRegistrationBean().apply { filter = DelegatingFilterProxy("shiroFilter") isEnabled = true addUrlPatterns("/*") setDispatcherTypes(DispatcherType.REQUEST) } } @Bean(name = ["shiroFilter"]) @Order(2) fun shiroFilter(): ShiroFilterFactoryBean { return ShiroFilterFactoryBean().apply { securityManager = securityManager() // loginUrl = "/login" // unauthorizedUrl = "/unauthor" filters = hashMapOf( "canon" to ZenCorsAnonymousFilter(), "cors" to ZenCorsPathMatchingFilter(), "perms" to UrlPermissionsFilter(), "authc" to AjaxAuthorizationFilter(), "anon" to AnonymousFilter() ) /** * anon(匿名) org.apache.shiro.web.filter.authc.AnonymousFilter * authc(身份验证) org.apache.shiro.web.filter.authc.FormAuthenticationFilter * authcBasic(http基本验证) org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter * logout(退出) org.apache.shiro.web.filter.authc.LogoutFilter * noSessionCreation(不创建session) org.apache.shiro.web.filter.session.NoSessionCreationFilter * perms(许可验证) org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter * port(端口验证) org.apache.shiro.web.filter.authz.PortFilter * rest (rest方面) org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter * roles(权限验证) org.apache.shiro.web.filter.authz.RolesAuthorizationFilter * ssl (ssl方面) org.apache.shiro.web.filter.authz.SslFilter * member (用户方面) org.apache.shiro.web.filter.authc.UserFilter * user 表示用户不一定已通过认证,只要曾被Shiro记住过登录状态的用户就可以正常发起请求,比如rememberMe */ filterChainDefinitionMap = linkedMapOf( // BUG 此处一定要使用 linkedHashMap 否则顺序有问题 "/auth/setcookie" to "canon", // 设置cookie "/auth/check" to "canon", // 检查登录状态 // "/auth/logout" to "logout", // 登出 "/test/**" to "canon", // 测试 免登录 "/upload/**" to "canon", // 上传免登录 "/api/**" to "canon", // api 免登陆 ////////////////////// 静态资源 ///////////////////// "/v2/api-docs" to "canon", "/swagger-resources/**" to "anon", "/swagger-ui.html" to "anon", "/webjars*" to "anon", "/webjars/**" to "anon", "/druid/**" to "anon", "/druid/sql.json" to "anon", "/**/favicon.*" to "anon", ////////////////////// 静态资源 ///////////////////// "/**" to "cors,authc,perms" // 对于其他未配置的所有url 先设置cors头 再进行登陆判定 最后判定权限 ) } } @Bean(name = ["securityManager"]) fun securityManager(): DefaultWebSecurityManager { return DefaultWebSecurityManager().apply { setRealm(userRealm()) cacheManager = redisCacheManager() sessionManager = defaultWebSessionManager() } } @Bean(name = ["sessionManager"]) fun defaultWebSessionManager(): ZenWebSessionManager { return ZenWebSessionManager().apply { setCacheManager(redisCacheManager()) globalSessionTimeout = 604800 * 1000 isDeleteInvalidSessions = true isSessionValidationSchedulerEnabled = true isDeleteInvalidSessions = true sessionDAO = redisSessionDAO() sessionIdCookie = SimpleCookie(ZenHttpSession.DEFAULT_SESSION_ID_NAME).apply { isHttpOnly = true maxAge = 604800 } } } @Bean fun redisSessionDAO(): ShiroRedisSessionDAO { return ShiroRedisSessionDAO(redisTemplate(), shiroRedisProperties()) } @Bean @DependsOn(value = ["shiroLifecycleBeanPostProcessor", "shrioRedisCacheManager"]) fun userRealm(): ZenShiroRealm { return ZenShiroRealm().apply { cacheManager = redisCacheManager() isCachingEnabled = true isAuthenticationCachingEnabled = true isAuthorizationCachingEnabled = true //TODO 以下 hash 验证,后期的重试 ban 可重写此类实现 credentialsMatcher = HashedCredentialsMatcher().also { it.hashAlgorithmName = "md5" it.hashIterations = 2 // 两次md5 } } } @Bean(name = ["shrioRedisCacheManager"]) @DependsOn(value = ["shiroRedisTemplate"]) fun redisCacheManager(): ShiroRedisCacheManager { return ShiroRedisCacheManager(redisTemplate(), shiroRedisProperties()) } @Bean(name = ["shiroRedisTemplate"]) fun redisTemplate(): RedisTemplate { return RedisTemplate().apply { connectionFactory = connectionFactory() val stringSerializer = StringRedisSerializer() keySerializer = stringSerializer valueSerializer = JdkSerializationRedisSerializer() hashKeySerializer = stringSerializer hashValueSerializer = Jackson2JsonRedisSerializer(Any::class.java) } } @Bean("shiroRedisConnectionFactory") fun connectionFactory(): JedisConnectionFactory { val properties = shiroRedisProperties() return JedisConnectionFactory().apply { database = properties.database hostName = properties.host password = properties.password port = properties.port timeout = properties.timeout } } @Bean("shiroLifecycleBeanPostProcessor") fun lifecycleBeanPostProcessor(): LifecycleBeanPostProcessor { return LifecycleBeanPostProcessor() } }