package cn.com.suntree.treetask.config;


import cn.com.suntree.common.ding.model.DingUserDo;
import cn.com.suntree.common.entity.SysUser;
import cn.com.suntree.common.entity.returnvo.PowerVo;
import cn.com.suntree.common.entity.returnvo.RoleVo;
import cn.com.suntree.common.mapper.DingUserMapper;
import cn.com.suntree.common.mapper.RoleMapper;
import cn.com.suntree.common.service.CmpUserService;
import cn.com.suntree.common.service.MenuService;
import cn.com.suntree.common.utils.YAMLUtils;
import cn.com.suntree.utils.myself.CommonUtil;
import lombok.extern.log4j.Log4j2;
import org.apache.shiro.authc.*;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.UnknownSessionException;
import org.apache.shiro.session.mgt.ExecutorServiceSessionValidationScheduler;
import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.crazycake.shiro.SerializeUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;

import javax.servlet.Filter;
import java.io.Serializable;
import java.util.*;

@Log4j2
@Configuration
public class ShiroConfig {
/**
* @param securityManager
* @return 拦截工厂配置
*/
@Bean
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// 必须设置 SecurityManager
shiroFilterFactoryBean.setSecurityManager(securityManager);
//自定义拦截器
Map<String, Filter> filtersMap = new LinkedHashMap<String, Filter>();
shiroFilterFactoryBean.setFilters(filtersMap);
//权限控制map
HashMap<String, String> filterMap = new LinkedHashMap<>();
filterMap.put("/api/enclosure/download", "anon");
filterMap.put("/api/register/**", "anon")
filterMap.put("/api/delay/**","anon");
filterMap.put("/api/**", "authc");

shiroFilterFactoryBean.setLoginUrl("/login");
shiroFilterFactoryBean.setUnauthorizedUrl("/codelogin");//验证码登陆
shiroFilterFactoryBean.setUnauthorizedUrl("/ddlogin/**");//验证码登陆
shiroFilterFactoryBean.setUnauthorizedUrl("/error");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);

return shiroFilterFactoryBean;
}

/**
* @return 安全管理器
*/
@Bean
public SecurityManager securityManager(@Qualifier("myShiroRealm") MyShiroRealm myShiroRealm) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();

// 自定义缓存实现 使用redis
securityManager.setCacheManager(cacheManager());
// 自定义session管理 使用redis
securityManager.setSessionManager(sessionManager());
// 设置realm.
securityManager.setRealm(myShiroRealm);
return securityManager;
}

/**
* 身份认证realm; (这个需要自己写,账号密码校验;权限等)
*
* @return
*/
@Bean
public MyShiroRealm myShiroRealm() {
MyShiroRealm myShiroRealm = new MyShiroRealm();
myShiroRealm.setCredentialsMatcher(credentialsMatcher());
myShiroRealm.setCacheManager(cacheManager());//設置緩存
return myShiroRealm;
}

/**
* cacheManager 缓存 redis实现
* 使用的是shiro-redis开源插件
*
* @return
*/
public RedisCacheManager cacheManager() {
RedisCacheManager redisCacheManager = new RedisCacheManager();
redisCacheManager.setRedisManager(redisManager());
return redisCacheManager;
}

/**
* 配置shiro redisManager
* 使用的是shiro-redis开源插件
*
* @return
*/
public RedisManager redisManager() {
RedisManager redisManager = new RedisManager();
redisManager.setHost(host);
redisManager.setPort(port);
redisManager.setExpire(expireTm);// 配置缓存过期时间
redisManager.setTimeout(0);
redisManager.setPassword(passwd);
return redisManager;
}

//浏览器会话的cookie管理
@Bean(name = "sessionIdCookie")
public SimpleCookie sessionIdCookie() {
SimpleCookie cookie = new SimpleCookie();
cookie.setName("WEBS");
cookie.setHttpOnly(true);
cookie.setMaxAge(-1);//浏览器关闭时失效此Cookie;
return cookie;
}

/**
* Session Manager
* 使用的是shiro-redis开源插件
*/
@Bean
public DefaultWebSessionManager sessionManager() {
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
sessionManager.setGlobalSessionTimeout(28800000L);
// sessionManager.setGlobalSessionTimeout(60000L);
sessionManager.setSessionIdCookieEnabled(true);
sessionManager.setSessionValidationScheduler(getExecutorServiceSessionValidationScheduler());
sessionManager.setSessionIdCookie(sessionIdCookie());
sessionManager.setSessionDAO(redisSessionDAO());
return sessionManager;
}

@Bean(name = "sessionValidationScheduler")
public ExecutorServiceSessionValidationScheduler getExecutorServiceSessionValidationScheduler() {
ExecutorServiceSessionValidationScheduler scheduler = new ExecutorServiceSessionValidationScheduler();
scheduler.setInterval(900000);
return scheduler;
}

/**
* 区分趣任务和趣乐部登陆session
*/
public class MyRedisSessionDAO extends RedisSessionDAO {

private Logger logger = LoggerFactory.getLogger(MyRedisSessionDAO.class);
private RedisManager redisManager;
//趣任务登陆会话所属key前缀
private String keyPrefix = "cmp_shiro_redis_session:";


public MyRedisSessionDAO() {
}

public void update(Session session) throws UnknownSessionException {
this.saveSession(session);
}

private void saveSession(Session session) throws UnknownSessionException {
if (session != null && session.getId() != null) {
byte[] key = this.getByteKey(session.getId());
byte[] value = SerializeUtils.serialize(session);
session.setTimeout((long) (this.redisManager.getExpire() * 1000));
this.redisManager.set(key, value, this.redisManager.getExpire());
} else {
logger.error("session or session id is null");
}
}

public void delete(Session session) {
if (session != null && session.getId() != null) {
this.redisManager.del(this.getByteKey(session.getId()));
} else {
logger.error("session or session id is null");
}
}

public Collection<Session> getActiveSessions() {
Set<Session> sessions = new HashSet();
Set<byte[]> keys = this.redisManager.keys(this.keyPrefix + "*");
if (keys != null && keys.size() > 0) {
Iterator i$ = keys.iterator();

while (i$.hasNext()) {
byte[] key = (byte[]) i$.next();
Session s = (Session) SerializeUtils.deserialize(this.redisManager.get(key));
sessions.add(s);
}
}

return sessions;
}

protected Serializable doCreate(Session session) {
Serializable sessionId = this.generateSessionId(session);
this.assignSessionId(session, sessionId);
this.saveSession(session);
return sessionId;
}

protected Session doReadSession(Serializable sessionId) {
if (sessionId == null) {
logger.error("session id is null");
return null;
} else {
Session s = (Session) SerializeUtils.deserialize(this.redisManager.get(this.getByteKey(sessionId)));
return s;
}
}

private byte[] getByteKey(Serializable sessionId) {
String preKey = this.keyPrefix + sessionId;
return preKey.getBytes();
}

public RedisManager getRedisManager() {
return this.redisManager;
}

public void setRedisManager(RedisManager redisManager) {
this.redisManager = redisManager;
this.redisManager.init();
}

public String getKeyPrefix() {
return this.keyPrefix;
}

public void setKeyPrefix(String keyPrefix) {
this.keyPrefix = keyPrefix;
}


}

/**
* RedisSessionDAO shiro sessionDao层的实现 通过redis
* 使用的是shiro-redis开源插件
*/
@Bean
public MyRedisSessionDAO redisSessionDAO() {
MyRedisSessionDAO redisSessionDAO = new MyRedisSessionDAO();
//自定义sessionId生成器
//redisSessionDAO.setSessionIdGenerator(mySessionIdGenerstor());
redisSessionDAO.setRedisManager(redisManager());
return redisSessionDAO;
}
/*
@Bean
public MySessionIdGenerstor mySessionIdGenerstor(){
return new MySessionIdGenerstor();
}*/


/***
* 授权所用配置
*
* @return
*/
@Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
defaultAAP.setProxyTargetClass(true);
return defaultAAP;
}

/**
* @param securityManager
* @return 授权注解支持
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}

/**
* Shiro生命周期处理器

@Bean public LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
*/
/**
* @return 密码匹配器
*/
@Bean
public CredentialsMatcher credentialsMatcher() {
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
hashedCredentialsMatcher.setHashAlgorithmName("md5");
return hashedCredentialsMatcher;
}


/**
* 自定义 - 数据域
*/
public class MyShiroRealm extends AuthorizingRealm {

@Autowired
@Lazy
private CmpUserService userService;
@Autowired
@Lazy
private RoleMapper roleMapper;

/**
* @param principalCollection
* @return
* @implNote 功能授权
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
SysUser user = (SysUser) principalCollection.getPrimaryPrincipal();
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
/* user = userService.getUserDetailInfoByUserId(user.getUserID(), user.getCompanyId());
List<RoleVo> roleList = user.getRoleList();
for (RoleVo role : roleList) {
authorizationInfo.addRole(role.getRoleName());
if (CommonUtil.check(role.getPowerList())) {
for (PowerVo permission : role.getPowerList()) {
authorizationInfo.addStringPermission(permission.getUrl());
}
}
}*/
authorizationInfo.setRoles(new HashSet<>(userService.listRoleNameByUser(user)));
authorizationInfo.setStringPermissions(new HashSet<>(userService.listPowerNameByUser(user)));
return authorizationInfo;
}

/**
* @param authenticationToken
* @return
* @throws AuthenticationException
* @implNote 身份认证
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
String key = (String) authenticationToken.getPrincipal();

boolean isPhomeToken = authenticationToken instanceof AuthenticationTokenMobilePhone;
SysUser u = new SysUser();
u.setAccNum(key);
SysUser user = isPhomeToken ? ((AuthenticationTokenMobilePhone) authenticationToken).getUser() : userService.getUserByAcc(u);
if (user == null) {
throw new UnknownAccountException();
} else if ("0".equals(user.getIsLock())) {
throw new LockedAccountException(); // 帐号冻结,非正常
}
String credentials = isPhomeToken ?
"d036a5788f9ffade1893be5eb5cee798" : user.getPasswd();
if(!CommonUtil.checkStr(credentials) || user.getIsActive()==0){

throw new AuthenticationException("该账号未激活,请先注册激活");
}
SimpleAuthenticationInfo simpleAuthenticationInfo =
new SimpleAuthenticationInfo(user, credentials, this.getName());
return simpleAuthenticationInfo;
}

@Override
public boolean supports(AuthenticationToken token) {// 支持多种验证因子

return true;
}
}
}