修改记录-优化后(springboot+shiro+session+redis+ngnix共享)

1.普通用户实现redis共享session

1.配置

#cache指定缓存类型
spring.cache.type=REDIS

#data-redis
spring.redis.database=15  //单节点配置  可择库
spring.redis.password=
spring.redis.host=192.168.210.*** //单节点配置
spring.redis.port=6379
spring.redis.timeout=2000
spring.redis.jedis.pool.max-active=8
spring.redis.jedis.pool.max-idle=8
spring.redis.jedis.pool.max-wait=-1
spring.redis.jedis.pool.min-idle=0
#集群配置 配置后单节点失效 #spring.redis.cluster.nodes=192.168.43.**:7000,192.168.43.**:7001,192.168.43.**:7000,192.168.43.**:7001,192.168.43.**:7000,192.168.43.**:7001 #spring.redis.cluster.max-redirects=3
#主从节点配置 配置后单节点,集群配置都失效
#spring.redis.sentinel.master=mymaster
#spring.redis.sentinel.nodes=192.168.210.**\:26379
 
#session share unit MINUTES 指定session在redis中过期时间
session.timeout
=3
#cacheTimeOut unit MINUTES 指定权限信息在redis中过期时间
cache.timeout
=12

2.开启缓存

@SpringBootApplication
@EnableCaching //开启缓存
public class OneserviceManagerApplication {
    public static void main(String[] args) {
        SpringApplication.run(OneserviceManagerApplication.class, args);
    }
}

3.编写RedisConfig.java配置类,主要作用是对象序列化

package com.ch.evaluation.config.redis;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisConfig extends CachingConfigurerSupport {

    @Autowired
    private RedisConnectionFactory redisConnectionFactory;

    /**
     * 获取RedisTemplate对象,处理Redis数据,并且进行最佳序列化
     * @return
     */
    @Bean(name="redisTemplate")
    public RedisTemplate<String, Object> redisTemplate() {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        //手动序列化
        JdkSerializationRedisSerializer jdkSerializationRedisSerializer = new JdkSerializationRedisSerializer();
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        template.setKeySerializer(stringRedisSerializer);
        template.setValueSerializer(jdkSerializationRedisSerializer);
        template.setHashKeySerializer(stringRedisSerializer);
        template.setHashValueSerializer(jdkSerializationRedisSerializer);
        //连接Redis
        template.setConnectionFactory(redisConnectionFactory);
        template.afterPropertiesSet();
        return template;
    }
}

4.RedisSessionDao的自定义实现(session的缓存处理)注意修改redis缓存的项目名


package com.ch.evaluation.auth.shiro.cas;

import com.ch.evaluation.common.util.PropertityUtil;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.UnknownSessionException;
import org.apache.shiro.session.mgt.eis.AbstractSessionDAO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;

import java.io.Serializable;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
* @description:SessionDao自定义实现
* @author: wangwei
* @date: 2018年11月27日
*/
@SuppressWarnings("all")
public class RedisSessionDao extends AbstractSessionDAO {
private final static String PREFIX="evaluation:shiro_redis_session:";
private static Logger logger = LoggerFactory.getLogger(RedisSessionDao.class);
private RedisTemplate redisTpl;

@Override
public void update(Session session) throws UnknownSessionException {
if (session==null || session.getId() == null){
logger.error("redis update session error:session or session id is null");
return;
}

try {
redisTpl.opsForValue().set(PREFIX+session.getId().toString(), session, PropertityUtil.getPropertity("session.timeout"), TimeUnit.MINUTES);
} catch (Exception e) {
logger.error(e.getMessage(), e);
throw new UnknownSessionException(e);
}
}

@Override
public void delete(Session session) {
if (session==null || session.getId() == null){
logger.error("redis delete session error:session or session id is null");
return;
}
try {
redisTpl.delete(PREFIX+session.getId().toString());
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}

@Override
public Collection<Session> getActiveSessions() {
Set<Session> sessions = new HashSet<Session>();
Set keys = redisTpl.keys(PREFIX+"*");

for(Object key : keys){
Session session=(Session) redisTpl.opsForValue().get(key);
sessions.add(session);
}
return sessions;
}

@Override
protected Serializable doCreate(Session session) {
if (session==null){
logger.error("redis create session error:session is null");
return null;
}
Serializable sessionId = generateSessionId(session);
assignSessionId(session, sessionId);
redisTpl.opsForValue().set(PREFIX+sessionId.toString(), session, PropertityUtil.getPropertity("session.timeout"), TimeUnit.MINUTES);
return sessionId;
}

@Override
protected Session doReadSession(Serializable sessionId) {
if (sessionId == null){
logger.error("redis read session error:sessionId is null");
return null;
}
Session session = null;
try {
session = (Session) redisTpl.opsForValue().get(PREFIX+sessionId);
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
return session;
}

public void setRedisTpl(RedisTemplate redisTpl) {
this.redisTpl = redisTpl;
}
}
 

5.上面用到了一个工具类加载配置文件

package com.ch.evaluation.common.util;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

public class PropertityUtil {

    public static int getPropertity(String key){
        Properties properties = new Properties();
        ClassLoader load = PropertityUtil.class.getClassLoader();
        InputStream is = load.getResourceAsStream("application.properties");
        try {
            properties.load(is);
            String value = properties.getProperty(key);
            int val = 0;
            if(value!=null){
               val = Integer.parseInt(value);
            }
            return val;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return 0;
    }


}

7..RedisCache的自定义实现(对权限和认证信息的缓存处理)注意修改redis缓存的项目名

package com.ch.evaluation.common.redis;

import com.ch.evaluation.common.util.PropertityUtil;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * Redis缓存类
 * Created by 005803 on 2017/10/12.
 */

public class RedisCache<K, V> implements Cache<K, V> {

    private RedisTemplate redisTemplate;
    private final static long SUPER_AMDIN_TICKET_EXPARE_TIME =3;
    private static final String PREFIX = "evaluation:shiro_redis_cache:";
    private static final String SUPER_TICKET_KEY = "evaluation:super_ticket:";

    public RedisCache(RedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    @Override
    public V get(K key) throws CacheException {
        return (V) redisTemplate.opsForValue().get(PREFIX + key);
    }

    @Override
    public V put(K key, V value) throws CacheException {
        redisTemplate.opsForValue().set(PREFIX + key, value, PropertityUtil.getPropertity("cache.timeout"), TimeUnit.MINUTES);
        return value;
    }

    @Override
    public V remove(K key) throws CacheException {
        Object value = redisTemplate.opsForValue().get(PREFIX + key);
        redisTemplate.delete(PREFIX + key);
        return (V) value;
    }

    @Override
    public void clear() throws CacheException {
        redisTemplate.delete(keys());
    }

    @Override
    public int size() {
        return keys().size();
    }

    @Override
    public Set<K> keys() {
        Set keys = redisTemplate.keys(PREFIX + "*");
        return keys != null ? keys : Collections.<K>emptySet();
    }

    @Override
    public Collection<V> values() {
        Set<K> keys = keys();
        Collection<V> c = new HashSet<>();
        for (K key : keys) {
            c.add((V) redisTemplate.opsForValue().get(key));
        }
        return c;
    }

    public V putSuper(K key, V value) throws CacheException {
        redisTemplate.opsForHash().put(SUPER_TICKET_KEY,key,value);
        redisTemplate.expire(SUPER_TICKET_KEY,SUPER_AMDIN_TICKET_EXPARE_TIME,TimeUnit.MINUTES);
        return value;
    }

    public Set<V> getAllSuperKeys() throws CacheException {
        return redisTemplate.opsForHash().keys(SUPER_TICKET_KEY);
    }

    public V getSuper(K key) throws CacheException {
        return (V) redisTemplate.opsForHash().get(SUPER_TICKET_KEY,key);
    }

    public void deleteSuper(K key) throws CacheException {
        redisTemplate.opsForHash().delete(SUPER_TICKET_KEY,key);
    }

    public boolean hasKey(K key) throws CacheException {
        return redisTemplate.opsForHash().hasKey(SUPER_TICKET_KEY,key);
    }
}

8..Redis缓存管理器的配置RedisCacheManager.java

package com.ch.evaluation.common.redis;

import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import org.apache.shiro.cache.CacheManager;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * Redis缓存管理器
 * Created by wangwei on 2018/10/19.
 */
public class RedisCacheManager implements CacheManager {

    private RedisTemplate redisTemplate;
    private final ConcurrentMap<String, Cache> caches = new ConcurrentHashMap<>();

    public void setRedisTemplate(RedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    @Override
    public <K, V> Cache<K, V> getCache(String name) throws CacheException {
        Cache cache = caches.get(name);
        if (cache == null) {
            cache = new RedisCache(redisTemplate);
            caches.put(name, cache);
        }
        return cache;
    }

}

9.在你自定义的shiro的realm中重写key的策略

public class ExtendCasRealm extends CasRealm {
    
    private static Logger LOGGER = LoggerFactory.getLogger(ExtendCasRealm.class);
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
         ......................
    }
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        ........................
    }
     ....................
     ..................
    @Override
    protected Object getAuthorizationCacheKey(PrincipalCollection principals) {
        return principals.getPrimaryPrincipal() + ":authorization";
    }

    @Override
    protected Object getAuthenticationCacheKey(PrincipalCollection principals) {
        return principals.getPrimaryPrincipal() + ":authentication";
    }

    @Override
    protected Object getAuthenticationCacheKey(AuthenticationToken token) {
        return token.getPrincipal() + ":authentication";
    }
}

10.基本上配置以上信息就可以用了,值得注意的是要在ShiroCasConfig中配置这些Bean的关联关系,记得session的获取方式有两种,一种是servlet的session一种是shiro默认的session管理器DefaultWebSessionManager ,我们要记得注入DefaultWebSessionManager 管理器,不然程序执行过程中可能会默认执行isServletContainerSessions方法导致抛出一个session类型的异常

贴一下ShiroCasConfig配置

package com.ch.evaluation.config.shirocas;

import com.ch.evaluation.auth.shiro.cas.ExtendCasRealm;
import com.ch.evaluation.auth.shiro.cas.RedisSessionDao;
import com.ch.evaluation.auth.shiro.filter.ExtendAuthorizationFilter;
import com.ch.evaluation.auth.shiro.filter.ExtendCasFilter;
import com.ch.evaluation.auth.shiro.filter.ExtendLogoutFilter;
import com.ch.evaluation.auth.shiro.service.IAuthorizationService;
import com.ch.evaluation.common.constants.WebConstants;
import com.ch.evaluation.common.redis.RedisCacheManager;
import org.apache.shiro.cache.MemoryConstrainedCacheManager;
import org.apache.shiro.cas.CasSubjectFactory;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.jasig.cas.client.session.SingleSignOutFilter;
import org.jasig.cas.client.session.SingleSignOutHttpSessionListener;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.filter.DelegatingFilterProxy;

import javax.servlet.Filter;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;


/**
 * Created by sunyong - 20170906
 */
@Configuration
public class ShiroCasConfig {

    @Value("${cas.server.url}")
    private String casServerUrl;

    @Value("${shiro.cas-server}")
    private String casServerUrlPrefix;

    @Value("${shiro.server}")
    private String shiroServerUrlPrefix;

    private static final String CAS_FILTER_NAME = "casFilter";
    private static final String SHIRO_FILTER_NAME = "shiroFilter";
    private static final String AUTH_FILTER_NAME = "authFilter";
    private static final String LOGOUT_FILTER_NAME = "logoutFilter";

    /**
     * 注册DelegatingFilterProxy(Shiro)
     */
    @Bean
    public FilterRegistrationBean<DelegatingFilterProxy> filterRegistrationBean() {
        FilterRegistrationBean<DelegatingFilterProxy> filterRegistration = new FilterRegistrationBean<DelegatingFilterProxy>();
        filterRegistration.setFilter(new DelegatingFilterProxy(SHIRO_FILTER_NAME));
        filterRegistration.addInitParameter("targetFilterLifecycle", "true");
        filterRegistration.setEnabled(true);
        filterRegistration.addUrlPatterns("/*");
        return filterRegistration;
    }

    /**
     * @param securityManager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor(
            DefaultWebSecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

    /**
     * 会话管理器
     * @auth 011336
     * @DATE 2018/10/15
     * @return
     */
    @Bean(name = "sessionManager")
    public DefaultWebSessionManager getDefaultWebSessionManager(RedisSessionDao sessionDAO, RedisCacheManager redisCacheManager) {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        //sessionManager.setGlobalSessionTimeout(sessionTimeout);
        sessionManager.setDeleteInvalidSessions(true);
        //sessionManager.setSessionValidationSchedulerEnabled(true);
        sessionManager.setSessionDAO(sessionDAO);
        sessionManager.setCacheManager(redisCacheManager);
        // TODO simpleCookie
        return sessionManager;
    }

    /**
     * 实例化SecurityManager,该类是shiro的核心类
     *
     * @return
     */
    @Bean
    public DefaultWebSecurityManager getDefaultWebSecurityManager(ExtendCasRealm extendCasRealm,
                    DefaultWebSessionManager sessionManager, RedisCacheManager redisCacheManager) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(extendCasRealm);
        securityManager.setCacheManager(redisCacheManager);
        securityManager.setSessionManager(sessionManager);
        securityManager.setSubjectFactory(new CasSubjectFactory());
        return securityManager;
    }

    /**
     * RedisSessionDao
     * @auth 011336
     * @DATE 2018/10/15
     * @return
     */
    @Bean
    public RedisSessionDao getRedisSessionDao(RedisTemplate redisTemplate) {
        RedisSessionDao sessionDAO = new RedisSessionDao();
        sessionDAO.setRedisTpl(redisTemplate);
        return sessionDAO;
    }

    /**
     * redisCacheManager
     * @auth 011336
     * @DATE 2018/10/15
     * @return
     */
    @Bean
    public RedisCacheManager getRedisCacheManager(RedisTemplate redisTemplate) {
        RedisCacheManager redisCacheManager = new RedisCacheManager();
        redisCacheManager.setRedisTemplate(redisTemplate);
        return redisCacheManager;
    }

    /**
     * 配置Realm,由于我们使用的是CasRealm,所以已经集成了单点登录的功能
     *
     * @param authorizationService
     * @return
     */
    @Bean
    public ExtendCasRealm getExtendCasRealm(IAuthorizationService authorizationService,
                                            RedisCacheManager redisCacheManager ){
        ExtendCasRealm extendCasRealm = new ExtendCasRealm();
        extendCasRealm.setAuthorizationService(authorizationService);
        // cas登录服务器地址前缀
        extendCasRealm.setCasServerUrlPrefix(casServerUrlPrefix);
        // 客户端回调地址,登录成功后的跳转地址(自己的服务地址)
        extendCasRealm.setCasService(shiroServerUrlPrefix + WebConstants.CAS_FILTER_URI);
        extendCasRealm.setCachingEnabled(true);
        extendCasRealm.setAuthenticationCachingEnabled(true);
        extendCasRealm.setAuthenticationCacheName("authenticationCache");
        extendCasRealm.setAuthorizationCachingEnabled(true);
        extendCasRealm.setAuthorizationCacheName("authorizationCache");
        extendCasRealm.setCacheManager(redisCacheManager);
        return extendCasRealm;
    }

    /**
     * 注册单点登出的listener
     *
     * @return
     */
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE) // 优先级需要高于Cas的Filter
    public ServletListenerRegistrationBean<SingleSignOutHttpSessionListener> singleSignOutHttpSessionListener() {
        ServletListenerRegistrationBean<SingleSignOutHttpSessionListener> bean = new ServletListenerRegistrationBean<SingleSignOutHttpSessionListener>();
        bean.setListener(new SingleSignOutHttpSessionListener());
        bean.setEnabled(true);
        return bean;
    }

    /**
     * 注册单点登出filter
     *
     * @return
     */
    @Bean
    public FilterRegistrationBean<SingleSignOutFilter> singleSignOutFilter() {
        FilterRegistrationBean<SingleSignOutFilter> bean = new FilterRegistrationBean<SingleSignOutFilter>();
        bean.setName("singleSignOutFilter");
        bean.setFilter(new SingleSignOutFilter());
        bean.addUrlPatterns("/*");
        bean.setEnabled(true);
        return bean;
    }

    /**
     * CAS过滤器
     *
     * @return
     */
    //@Bean(name = CAS_FILTER_NAME)
    public ExtendCasFilter getExtendCasFilter() {
        ExtendCasFilter casFilter = new ExtendCasFilter();
        casFilter.setName(CAS_FILTER_NAME);
        casFilter.setEnabled(true);
        // String loginUrl = casServerUrl + "/login?service=" + shiroServerUrlPrefix + CAS_FILTER_URI;
        casFilter.setFailureUrl("/error/casfailure");
        casFilter.setExtendFailureUrl("/error/casfailure"); // 由于原failuserUrl为私有字段,在扩展类中不能获取到值
        return casFilter;
    }

    /**
     * extAuth Filter
     */
    //@Bean(name = AUTH_FILTER_NAME)
    public ExtendAuthorizationFilter getExtendAuthorizationFilter(
            IAuthorizationService authorizationService) {
        ExtendAuthorizationFilter extAuthFilter = new ExtendAuthorizationFilter();
        extAuthFilter.setName(AUTH_FILTER_NAME);
        extAuthFilter.setEnabled(true);
        extAuthFilter.setAuthorizationService(authorizationService);
        return extAuthFilter;
    }

    /**
     * extLogout Filter
     */
    //@Bean(name = LOGOUT_FILTER_NAME)
    public ExtendLogoutFilter getExtendLogoutFilter(IAuthorizationService authorizationService) {
        ExtendLogoutFilter extLogoutFilter = new ExtendLogoutFilter();
        extLogoutFilter.setName(LOGOUT_FILTER_NAME);
        extLogoutFilter.setEnabled(true);
        extLogoutFilter.setAuthorizationService(authorizationService);
        extLogoutFilter.setRedirectUrl(casServerUrl + "/logout?service=" + shiroServerUrlPrefix);
        return extLogoutFilter;
    }

    /**
     * 使用工厂模式,创建并初始化ShiroFilter
     *
     * @param securityManager
     * @param authorizationService
     * @return
     */
    @Bean(name = SHIRO_FILTER_NAME)
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager securityManager,
                                                            IAuthorizationService authorizationService) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        String loginUrl = casServerUrl + "/login?service=" + shiroServerUrlPrefix + WebConstants.CAS_FILTER_URI;
        shiroFilterFactoryBean.setLoginUrl(loginUrl);
        shiroFilterFactoryBean.setSuccessUrl("/");
        shiroFilterFactoryBean.setUnauthorizedUrl("/error/unauthorized");
        Map<String, Filter> filters = new HashMap<>();
        filters.put(CAS_FILTER_NAME, getExtendCasFilter());
        filters.put(LOGOUT_FILTER_NAME, getExtendLogoutFilter(authorizationService));
        filters.put(AUTH_FILTER_NAME, getExtendAuthorizationFilter(authorizationService));
        shiroFilterFactoryBean.setFilters(filters);

        loadShiroFilterChain(shiroFilterFactoryBean);
        return shiroFilterFactoryBean;
    }

    private void loadShiroFilterChain(ShiroFilterFactoryBean shiroFilterFactoryBean) {
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        filterChainDefinitionMap.put(WebConstants.CAS_FILTER_URI, CAS_FILTER_NAME);
        filterChainDefinitionMap.put("/logout", LOGOUT_FILTER_NAME);
        filterChainDefinitionMap.put("/static/**", "anon");
        filterChainDefinitionMap.put("/css/**", "anon");
        filterChainDefinitionMap.put("/front/**", "anon");
        filterChainDefinitionMap.put("/img/**", "anon");
        filterChainDefinitionMap.put("/js/**", "anon");
        filterChainDefinitionMap.put("/plugin/**", "anon");
        filterChainDefinitionMap.put("/home/**", "anon");
        filterChainDefinitionMap.put("/super", "anon");
        filterChainDefinitionMap.put("/interface/**", "anon");
        filterChainDefinitionMap.put("/super/login", "anon");
        filterChainDefinitionMap.put("/error/**", "anon");
        filterChainDefinitionMap.put("/**", AUTH_FILTER_NAME);
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
    }
}

 

二:超管redis的session共享

1..把原来的SuperAdminTickeUtils更名为SuperAdminTicketManager,或者删除替换也行。记得把其他用到这个类的地方改一下就行,不详细说改了啥了。

package com.ch.evaluation.auth.shiro;

import com.ch.evaluation.auth.shiro.entity.AuthorizationUser;
import com.ch.evaluation.common.redis.RedisCache;
import com.ch.evaluation.common.redis.RedisCacheManager;
import org.jasig.cas.client.authentication.AttributePrincipalImpl;
import org.jasig.cas.client.validation.Assertion;
import org.jasig.cas.client.validation.AssertionImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.*;

@Component
public class SuperAdminTicketManager {
    private static Logger LOGGER = LoggerFactory.getLogger(SuperAdminTicketManager.class);
    private final static  String SUPER_AMDIN_TICKET_SUFFIX = ".superadmin.com";
    private final static long SUPER_AMDIN_TICKET_TIME = 1000 * 60 * 3;
    private final static String SUPER_TICKET = "superTicket";
    @Autowired
    private RedisCacheManager redisCacheManager;

    public String putTicket(AuthorizationUser user) {
        RedisCache cache = (RedisCache)redisCacheManager.getCache(SUPER_TICKET);
        String ticket = getTicket();
        cache.putSuper(ticket,user);
        return ticket;
    }

    public boolean validTiket(String ticket) {
        RedisCache cache = (RedisCache)redisCacheManager.getCache(SUPER_TICKET);
        clearTicketMap(cache);
        return cache.hasKey(ticket);
    }

    public boolean endsWith(String ticket) {
        return ticket.endsWith(SUPER_AMDIN_TICKET_SUFFIX);
    }

    private static String getTicket() {
        return UUID.randomUUID() + SUPER_AMDIN_TICKET_SUFFIX;
    }

    private void clearTicketMap(RedisCache cache) {
        Long currentTime = new Date().getTime();
        Set<String> keys= cache.getAllSuperKeys();
        for (Object key:keys) {
            AuthorizationUser user = (AuthorizationUser)cache.getSuper(key);
            if((currentTime - user.getTime()) > SUPER_AMDIN_TICKET_TIME){
                LOGGER.info("super.ticket has expired and delete from redis!");
                cache.deleteSuper(key);
            }
        }
    }

    public final Assertion getSuperAdminAssertion(String ticket) {
        Assertion assertion = null;
        final Map<String, Object> attributes = new HashMap<String, Object>();
        RedisCache cache = (RedisCache)redisCacheManager.getCache(SUPER_TICKET);
        AuthorizationUser user = (AuthorizationUser)cache.getSuper(ticket);
        if (null != user) {
            attributes.put("user_id", user.getUserId());
            attributes.put("user_name", user.getUserName());
            attributes.put("password", user.getPassword());
            assertion = new AssertionImpl(new AttributePrincipalImpl(user.getUserAccount(), attributes));
        }
        cache.deleteSuper(ticket);
        return assertion;
    }
}

2.修改之前用到这个类的地方

2.1 修改ExtendCasRealm  新增一处  改三处
@Autowired
private SuperAdminTicketManager superAdminTicketManager;

@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
CasToken casToken = (CasToken) token;
if (token == null) {
return null;
}
String ticket = (String)casToken.getCredentials();
if (!StringUtils.hasText(ticket)) {
return null;
}
boolean superAdminFlag = superAdminTicketManager.endsWith(ticket);
    ...........
private Assertion getSuperAdminAssertion(CasToken casToken, String ticket) 
throws CasAuthenticationException {
if (!superAdminTicketManager.validTiket(ticket)) {
throw new CasAuthenticationException("Invalid super ticket [" + ticket + "]");
}
Assertion casAssertion = superAdminTicketManager.getSuperAdminAssertion(ticket);
return casAssertion;
}

2.2SuperAdminServiceImpl 中新增一处  改一处

@Autowired
private SuperAdminTicketManager superAdminTicketManager;

....
    String ticket = superAdminTicketManager.putTicket(user);
return ticket;
}


}

3.序列化:实现接口后鼠标放在类上alt+Enter就可以生成uid

public class AuthorizationUser implements Serializable {

    private static final long serialVersionUID = -5556165398740497973L;

三:超管登录密码加密

1.引入js文件夹到plugin

2.layout-superlogin.html中引入JS

 <script class="custom-script" src="../../static/plugin/crypto-js/crypto-js.js" th:src="@{/plugin/crypto-js/crypto-js.js}"></script>
 <script class="custom-script" src="../../static/plugin/crypto-js/core.js" th:src="@{/plugin/crypto-js/core.js}"></script>
 <script class="custom-script" src="../../static/plugin/crypto-js/aes.js" th:src="@{/plugin/crypto-js/aes.js}"></script>

3.编写superlogin.js文件

复制代码
var key = CryptoJS.enc.Utf8.parse("1234123412ABCDEF");  //十六位十六进制数作为密钥
var iv = CryptoJS.enc.Utf8.parse('ABCDEF1234123412');   //十六位十六进制数作为密钥偏移量
//解密方法
function Decrypt(word) {
    var encryptedHexStr = CryptoJS.enc.Hex.parse(word);
    var srcs = CryptoJS.enc.Base64.stringify(encryptedHexStr);
    var decrypt = CryptoJS.AES.decrypt(srcs, key, { iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 });
    var decryptedStr = decrypt.toString(CryptoJS.enc.Utf8);
    return decryptedStr.toString();
}

//加密方法
function Encrypt(word) {
    var srcs = CryptoJS.enc.Utf8.parse(word);
    var encrypted = CryptoJS.AES.encrypt(srcs, key, { iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 });
    return encrypted.ciphertext.toString().toUpperCase();
}
function superLogin(){
    var passWord = $("#signin-superamdin-password").val();
    if(passWord==null || passWord==""){
        passWord ="";
    }else{
        passWord = passWord.trim();
    }
    var prefix = getTimeStr("prefix");
    var suffix = getTimeStr("suffix");
    passWord = prefix+passWord+suffix
    var aesPassWord = Encrypt(passWord);
    $("#submit-superamdin-password").val(aesPassWord);
    return true;
}
function getTimeStr(flag){
    var myDate = new Date();

    var year = myDate.getFullYear(); //获取完整的年份(4位,1970-????)
    var month = myDate.getMonth()+1; //获取当前月份(0-11,0代表1月)
    month = month > 9 ? month : "0"+month;
    var day =  myDate.getDate(); //获取当前日(1-31)
    day = day > 9 ? day : "0"+day;
    var hours =  myDate.getHours(); //获取当前小时数(0-23)
    hours = hours > 9 ? hours : "0"+hours;
    var minutes =  myDate.getMinutes(); //获取当前分钟数(0-59)
    minutes = minutes > 9 ? minutes : "0"+minutes;
    var seconds =  myDate.getSeconds(); //获取当前秒数(0-59)
    seconds = seconds > 9 ? seconds : "0"+seconds;
    if(flag=="prefix"){
        return ""+year+month+day
    }else{
        return ""+hours+minutes+seconds
    }
}
复制代码

3.1:替换html部分的form部分

 

4.可直接替换superloginController.java   详情如下

4.1:校验是否超时,获取时间差

复制代码
public boolean checkLogionTime(String rangeTime){
        String strDate = rangeTime;
        //注意:SimpleDateFormat构造函数的样式与strDate的样式必须相符
        SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyyMMddHHmmss");
        SimpleDateFormat df=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //加上时间
        //必须捕获异常
        Date date= null;
        try {
            date = simpleDateFormat.parse(strDate);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        long min = getDatePoor(new Date(),date);
        if(min>=loginTimeOut) {
            return false;
        }else{
            return true;
        }
    }
    //计算时间差  差多少分钟   绝对值
    public static long getDatePoor(Date endDate, Date loginDate) {
        long nd = 1000 * 24 * 60 * 60;
        long nh = 1000 * 60 * 60;
        long nm = 1000 * 60;
        // long ns = 1000;
        // 获得两个时间的毫秒时间差异
        long diff = endDate.getTime() - loginDate.getTime();
        // 计算差多少分钟
        long min = diff % nd % nh / nm;
        return Math.abs(min);
    }
复制代码

4.2:验证之前解密   直接加在原来的步骤中就好

复制代码
public String login(ModelMap modal, String superAdminUsername, String superAdminPassword,
                        HttpServletRequest request, HttpServletResponse response) {
        if (StringUtils.isNotBlank(superAdminUsername) && StringUtils.isNotBlank(superAdminPassword)) {
            try {
                String str = AesUtil.desEncrypt(superAdminPassword);
                superAdminPassword = str.substring(8, str.length() - 6);
                String rangeTime =  str.substring(0,8)+str.substring(str.length()-6);
                boolean b = checkLogionTime(rangeTime);
                if(!b) {
                    modal.put(ErrorConstants.ERROR_MESSAGE, ErrorConstants.ERROR_SUPERADMIN_003);
                    return "views/superLoginPage";
                }
            } catch (Exception e) {
                LOGGER.error("decrypt applicationMetadataId failed", e);
            }
            SuperAdmin superAdmin = new SuperAdmin();
            superAdmin.setUsername(superAdminUsername.trim());
复制代码

有个超时时间的设置在propertity中

# unit minutes
super.login.timeOut=5

5.后台要引入一个解密的Java工具类,ErrorConstants中加一个错误提示信息

public final static String ERROR_SUPERADMIN_003 = "登陆超时,不安全的请求!"; // 判断登录请求超过一定时间为不安全请求

 

 

完事!

 

四:单点登录ticket验证地址变更

1.ShiroCasConfig中假如如下配置

  @Value("${cas.server.url}")
  private String casServerUrl;

替换如下内容

复制代码
  //@Bean(name = LOGOUT_FILTER_NAME)
    public ExtendLogoutFilter getExtendLogoutFilter(IAuthorizationService authorizationService) {
        ExtendLogoutFilter extLogoutFilter = new ExtendLogoutFilter();
        extLogoutFilter.setName(LOGOUT_FILTER_NAME);
        extLogoutFilter.setEnabled(true);
        extLogoutFilter.setAuthorizationService(authorizationService);
        extLogoutFilter.setRedirectUrl(casServerUrl + "/logout?service=" + shiroServerUrlPrefix);
        return extLogoutFilter;
    }
@Bean(name = SHIRO_FILTER_NAME)
public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager securityManager,
IAuthorizationService authorizationService) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
String loginUrl = casServerUrl + "/login?service=" + shiroServerUrlPrefix + WebConstants.CAS_FILTER_URI;
shiroFilterFactoryBean.setLoginUrl(loginUrl);
 
复制代码

完事!

posted @ 2018-11-27 19:16  隔壁w王叔叔  阅读(1656)  评论(0编辑  收藏  举报