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);
完事!