package com.sso.controller;

import com.google.code.kaptcha.Producer;
import com.google.code.kaptcha.util.Config;
import com.njcn.enums.*;
import com.njcn.mapper.configuration.SysMapper;
import com.njcn.mapper.user.UserMapper;
import com.njcn.pojo.commons.HttpResult;
import com.njcn.pojo.commons.RedisDB;
import com.njcn.pojo.configuration.SysSet;
import com.njcn.pojo.system.SubSystem;
import com.njcn.service.log.UserLogDetailService;
import com.njcn.service.log.UserLoginService;
import com.njcn.service.system.SystemService;
import com.njcn.service.user.UserService;
import com.njcn.shiro.filter.KickoutSessionFilter;
import com.njcn.shiro.session.UserSessionManager;
import com.njcn.shiro.session.UserShiroSessionRepository;
import com.njcn.shiro.token.TokenManager;
import com.njcn.sso.pojo.user.User;
import com.njcn.sso.shiro.session.SessionStatus;
import com.njcn.utils.*;
import com.njcn.utils.redis.JedisManager;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AccountException;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.mgt.SimpleSession;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.subject.support.DefaultSubjectContext;
import org.apache.shiro.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.*;

/**
 * @author hongawen
 * @Date: 2018/9/18 11:36
 */
@Controller
@RequestMapping("/user")
public class UserController {

    private static final Logger logger = LoggerFactory.getLogger(UserController.class);

    @Autowired
    private JedisManager jedisManager;

    @Autowired
    private UserService userService;

    @Autowired
    private UserLoginService userLoginService;

    @Autowired
    private UserShiroSessionRepository userShiroSessionRepository;

    @Autowired
    private AppConfig appConfig;

    @Autowired
    private SpringPubUtils springPubUtils;

    @Autowired
    private UserUtil userUtil;

    @Autowired
    private SysMapper sysSetMapper;

    @Autowired
    private UserLogDetailService userLogDetailService;

    @Autowired
    private UserMapper userMapper;

    @Resource
    private SystemService systemService;


    /**
     * 根据token判断用户是否登录
     * 不登录便跳转登录页面
     * 登录后重定向用户原来的目标地址
     */
    @GetMapping("/judgeLogin")
    public void judgeLogin(HttpServletRequest request, HttpServletResponse response, String item) {
        try {
            String tokenValue = PubUtils.getSSOToken(request);
            if (StringUtils.isNotBlank(tokenValue)) {
                //首先根据token去redis中查询有没有对应的session
                Session session = userShiroSessionRepository.getSession(tokenValue);
                if (null == session) {
                    //会话过期，重新登录
                    redirectLogin(request, response);
                } else {
                    //用户登录后判断当前用户激活的系统，并从中选择一个默认子系统，如果当前激活的系统只有一个，则默认该系统
                    if (StringUtils.isEmpty(item)) {
                        List<SubSystem> subSystems = systemService.getActiveSystem();
                        if (subSystems.size() == 1) {
                            item = subSystems.get(0).getSystemEName();
                        } else {
                            for (SubSystem subSystem : subSystems) {
                                if (subSystem.getDefaultSystem() == 1) {
                                    item = subSystem.getSystemEName();
                                    break;
                                }
                            }
                        }
                    }
                    //会话没有过期，将会话ID和用户信息传递给目标项目,同时激活下当前session，延长周期
                    userShiroSessionRepository.activeSession(session);
                    session.setAttribute("item", "");
                    String url;
                    String baseUrl = request.getRequestURL().toString();
                    String nickname = TokenManager.getNickname();
                    if (SecurityUtils.getSubject().hasRole(RoleGroupEnum.SYSTEM.getEname()) || SecurityUtils.getSubject().hasRole(RoleGroupEnum.AUDIT.getEname()) || SecurityUtils.getSubject().hasRole(RoleGroupEnum.CHECK.getEname()) || SecurityUtils.getSubject().hasRole(RoleGroupEnum.BUSINESS.getEname()) || SecurityUtils.getSubject().hasRole(RoleGroupEnum.ROOT.getEname())) {
                        url = baseUrl.substring(0, baseUrl.indexOf(ProjectEnum.SSO.getItem())) + ProjectEnum.PQS9900.getLoginUrl() + tokenValue + "&subItem=" + item;
                    } else if (Objects.equals(nickname, appConfig.getThirdName())) {
                        url = baseUrl.substring(0, baseUrl.indexOf(ProjectEnum.SSO.getItem())) + ProjectEnum.PQS9200.getLoginUrl() + tokenValue;
                    } else {
                        if (item.equalsIgnoreCase(ProjectEnum.PQS9000.getItem())) {
                            url = baseUrl.substring(0, baseUrl.indexOf(ProjectEnum.SSO.getItem())) + ProjectEnum.PQS9000.getLoginUrl() + tokenValue;
                        } else if (item.equalsIgnoreCase(ProjectEnum.PQS9200.getItem())) {
                            url = baseUrl.substring(0, baseUrl.indexOf(ProjectEnum.SSO.getItem())) + ProjectEnum.PQS9200.getLoginUrl() + tokenValue;
                        } else {
                            url = baseUrl.substring(0, baseUrl.indexOf(ProjectEnum.SSO.getItem())) + ProjectEnum.PQS9300.getLoginUrl() + tokenValue;
                        }
                    }

                    //重定向到某子系统，进行登录认证
                    WebUtils.issueRedirect(request, response, url);
                }
            } else {
                //没有登录的token,则需要跳转到登录页面
                redirectLogin(request, response);
            }
        } catch (Exception e) {
            logger.error("判断登录异常，" + e.toString());
        }
    }

    /**
     * 获取图形验证码
     *
     * @param resp    响应体
     * @param session 当前会话
     */
    @PostMapping("/getImgCode")
    public void getImgCode(HttpServletResponse resp, HttpSession session) {
        ServletOutputStream out;
        try {
            out = resp.getOutputStream();
            if (null != out) {
                Properties props = new Properties();
                Producer kaptchaProducer;
                ImageIO.setUseCache(false);
                props.put("kaptcha.border", "no");
                props.put("kaptcha.textproducer.font.color", "black");
                /*props.put("kaptcha.obscurificator.impl", "com.google.code.kaptcha.impl.ShadowGimpy");*/
                /*props.put("kaptcha.noise.impl", "com.sso.utils.ComplexNoise");*/
                props.put("kaptcha.textproducer.char.space", "5");
                props.put("kaptcha.textproducer.char.length", "4");
                Config config = new Config(props);
                kaptchaProducer = config.getProducerImpl();
                //此处需要固定采用字母和数字混合
                String capText = getKatpchaText();
                BufferedImage bi = kaptchaProducer.createImage(capText);
                session.removeAttribute("capText");
                session.setAttribute("capText", capText);
                ImageIO.write(bi, "jpg", out);
            }
        } catch (IOException e1) {
            logger.error("登录界面获取图形验证码异常，原因为：" + e1.toString());
        }
    }

    /**
     * 自定义获取验证码，固定为字母和数字的组合
     */
    private String getKatpchaText() {
        String result = "";
        char[] letters = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'};
        char[] numbers = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
        Character[] text = new Character[4];
        //首先获取出需要几个字母，范围在1-3选择，最少一个字母，最多三个字母
        int letterNumbers = PubUtils.getRandomInt(3) + 1;
        //填充字母
        for (int i = 0; i < letterNumbers; i++) {
            int letterIndex = PubUtils.getRandomInt(26);
            text[i] = letters[letterIndex];
        }
        //填充数字
        for (int i = letterNumbers; i < 4; i++) {
            int numberIndex = PubUtils.getRandomInt(10);
            text[i] = numbers[numberIndex];
        }
        //填充完字符后，打乱顺序，返回字符串
        List<Character> textList = Arrays.asList(text);
        Collections.shuffle(textList);
        for (int i = 0; i < textList.size(); i++) {
            result = result + textList.get(i);
        }
        return result;
    }


    /**
     * 根据用户名重置密码
     *
     * @param username 用户名
     * @param password 密码
     */
    @PostMapping("modifyPwd")
    @ResponseBody
    public HttpResult modifyPwd(HttpServletRequest request, String username, String password) {
        username = XssFilterUtil.dealString(username);
        password = XssFilterUtil.dealString(password);
        username = DesUtils.aesDecrypt(username);
        HttpResult result = PubUtils.initResult(username, request, LogTypeEnum.SYSTEM.toString(), 1);
        String ip = PubUtils.getIpAddr(request);
        try {
            //密码处理
            String privateKey = jedisManager.getValueByKey(RedisDB.SHORT_TIME_QUERY, username + ip);
            //秘钥用完即删
            jedisManager.deleteByKey(RedisDB.SHORT_TIME_QUERY, username + ip);
            //对SM2解密面进行验证
            password = PubUtils.getPasswordSM2Verify(privateKey, password);

            if (password == null && password.length() == 0) {
                return PubUtils.assignmentResultLog(null, 500, "密码传输完整性被破坏", "密码传输遭破坏", "失败", result);
            }

            //增加判断密码是否包含用户名
            if (password.contains(username)) {
                return PubUtils.assignmentResult(null, 500, "用户密码包含登录名");
            }
            if (!(PubUtils.patternPassword(password) || PubUtils.patternUsername(username))) {
                result = PubUtils.assignmentResult(null, 500, "用户名密码有误");
                return result;
            }
            userService.modifyPassword(username, password);
            result = PubUtils.assignmentResultLog(null, 200, "修改密码成功", "修改密码", "成功", result);
        } catch (Exception e) {
            logger.error("更新密码出错，异常为：" + e.toString());
            String eMessage = e.getMessage();
            if (eMessage.length() > 20) {
                eMessage = "内部出错";
            }
            result = PubUtils.assignmentResultLog(null, 500, eMessage, "修改密码", "失败", result);
        }
        return result;
    }

    /************************
     * RSA
     * 根据用户名生成秘钥对
     * @param username 用户名
     *************************/
    @PostMapping("generateRSAKey")
    @ResponseBody
    public HttpResult generateRSAKey(String username, HttpServletRequest request) {
        HttpResult result;
        try {
            username = DesUtils.aesDecrypt(username);
            // 获取公钥和私钥
            HashMap<String, Object> keys = RSAUtils.getKeys();
            RSAPublicKey publicKey = (RSAPublicKey) keys.get("public");
            RSAPrivateKey privateKey = (RSAPrivateKey) keys.get("private");
            String ip = StringUtils.isBlank(PubUtils.getIpAddr(request)) ? "127.0.0.1" : PubUtils.getIpAddr(request);
            //秘钥先删除再添加
            jedisManager.deleteByKey(RedisDB.SHORT_TIME_QUERY, SerializeUtil.serialize(username + ip));
            // 保存私钥到 redis，也可以保存到数据库
            jedisManager.saveValueByKey(RedisDB.SHORT_TIME_QUERY, SerializeUtil.serialize(username + ip), SerializeUtil.serialize(privateKey), -1);
            // 将公钥传到前端
            Map<String, String> map = new HashMap<String, String>();
            // 注意返回modulus和exponent以16为基数的BigInteger的字符串表示形式
            map.put("modulus", publicKey.getModulus().toString(16));
            map.put("exponent", publicKey.getPublicExponent().toString(16));
            result = PubUtils.assignmentResult(map, 200, "生成秘钥对成功！");

            /*byte[] privateBT = privateKey.getEncoded();
            // base64
            saveFile("d://privateKey.txt", encodeBase64(privateBT));
            byte[] publicBT = publicKey.getEncoded();
            // base64
            saveFile("d://publicKey.txt", encodeBase64(publicBT));*/
        } catch (Exception u) {
            result = PubUtils.assignmentResult(null, 500, u.getMessage());
        }
        return result;
    }


    /************************
     * SM2
     * 根据用户名生成秘钥对
     * @param username 用户名
     *************************/
    @PostMapping("generateSM2Key")
    @ResponseBody
    public HttpResult generateSM2Key(String username, HttpServletRequest request) {
        HttpResult result;
        try {
            if (PubUtils.isBlank(username)) {
                result = PubUtils.assignmentResult(null, 500, "用户名非法！");
                return result;
            }
            username = DesUtils.aesDecrypt(username);
            // 获取公钥和私钥
            Map<String, String> keyMap = PubUtils.getSM2CipHer();
            String publicKey = keyMap.get("publicKey");  //加密密钥
            String privateKey = keyMap.get("privateKey"); //解密密钥
            String ip = PubUtils.isBlank(PubUtils.getIpAddr(request)) ? "127.0.0.1" : PubUtils.getIpAddr(request);
            //秘钥先删除再添加
            jedisManager.deleteByKey(RedisDB.SHORT_TIME_QUERY, username + ip);
            // 保存私钥到 redis，也可以保存到数据库
            jedisManager.saveValueByKey(RedisDB.SHORT_TIME_QUERY, username + ip, privateKey, -1);
            // 将公钥传到前端
            result = PubUtils.assignmentResult(publicKey, 200, "生成秘钥对成功！");
        } catch (Exception u) {
            result = PubUtils.assignmentResult(null, 500, u.getMessage());
        }
        return result;
    }

    /**
     * 用户登录入口
     * todo... 判断是否配置激活子系统
     */
    @RequestMapping(value = "login", method = RequestMethod.POST)
    @ResponseBody
    public HttpResult login(HttpServletRequest request, HttpServletResponse response, HttpSession session, String username, String password, String checkcode) {
        //Xss攻击处理
        username = XssFilterUtil.dealString(username);
        username = DesUtils.aesDecrypt(username);
        HttpResult result = PubUtils.initResult(username, request, LogTypeEnum.SYSTEM.toString(), 2);
        // 验证码校验
        if (!judgeCode(checkcode, session)) {
            return PubUtils.assignmentResult(null, 500, "验证码错误");
        }
        String ip = PubUtils.getIpAddr(request);
        //登录之前针对最大并发会话控制
        try {
            judgeLoginSession(username);
        } catch (Exception u) {
            String exeString = u.getMessage();
            if (exeString.length() > 20) {
                exeString = "登录出错，联系管理员";
            }
            return PubUtils.assignmentResultLog(null, 500, exeString, "超过系统最大并发", "失败", result);
        }
        try {
            System.out.println(password);
            //密码处理
            String privateKey = jedisManager.getValueByKey(RedisDB.SHORT_TIME_QUERY, username + ip);
            //秘钥用完即删
            jedisManager.deleteByKey(RedisDB.SHORT_TIME_QUERY, username + ip);
            //对SM2解密面进行验证
            password = PubUtils.getPasswordSM2Verify(privateKey, password);

            if (password == null || password.length() == 0) {
                return PubUtils.assignmentResultLog(null, 500, "密码传输完整性被破坏", "密码传输遭破坏", "失败", result);
            }
            //在登录之前，判断当前session是否存在用户信息，存在便进行一次子系统登出（认证中心的session里的用户信息进行更新时，子系统便需要注销，重新认证）
            User ssoUser = TokenManager.getToken();
            if (null != ssoUser) {
                //注销当前会话中旧用户信息和权限资源
                logoutSystem(session.getId(), ssoUser.getUserIndex());
            }
            TokenManager.login(username, "", password, ip, ProjectEnum.SSO.getItem());
            TokenManager.getSession().setAttribute(KickoutSessionFilter.KICKOUT_STATUS, null);
            //保存登录记录
            userLoginService.insertLogin(username, ProjectEnum.SSO.getItem());
            onlineSession();
            result = PubUtils.assignmentResult(null, 200, "登录成功");
            userLogDetailService.saveUserLog(TokenManager.getToken().getUserIndex(), TokenManager.getToken().getLoginName(), "用户登录", "成功", appConfig.getSystemFlag(), PubUtils.getIpAddr(request), LogTypeEnum.SYSTEM.toString(), 0);
            response.setStatus(203);
            String sessionId = TokenManager.getSession().getId().toString();
            TokenManager.getSession().setAttribute(CookieKeyEnum.SSOLOGIN.getCookieKey(), sessionId);
            //将认证后的session保存到cookie中
            saveTokenToCookie(request, response, sessionId);
            userUtil.saveRandomToken();
        } catch (Exception u) {
            logger.error("用户登录发生异常，异常为：" + u.toString());
            String exeString = u.getMessage();
            if (exeString.length() > 20) {
                exeString = "登录出错，联系管理员";
            }
            int level = 2;
            if (exeString.equalsIgnoreCase(UserStatusEnum.ERROR_ACCOUNT.getMessage())) {
                level = 1;
            }
            result = PubUtils.assignmentResult(null, 500, exeString);
            User user = new User();
            user.setLoginName(username);
            user = userMapper.selectOne(user);
            try {
                if (null == user) {
                    userLogDetailService.saveUserLog(username, username, exeString, "失败", appConfig.getSystemFlag(), PubUtils.getIpAddr(request), LogTypeEnum.SYSTEM.toString(), level);
                } else {
                    userLogDetailService.saveUserLog(user.getUserIndex(), user.getLoginName(), exeString, "失败", appConfig.getSystemFlag(), PubUtils.getIpAddr(request), LogTypeEnum.SYSTEM.toString(), level);
                }
            } catch (Exception e) {
                logger.error("用户异常登录日志入库出错，" + e.toString());
            }
        }
        return result;
    }


    /**
     * 免登录入口
     * 默认指定为省级用户
     */
    @RequestMapping(value = "visitor", method = RequestMethod.GET)
    @ResponseBody
    public HttpResult visitor(HttpServletRequest request, HttpServletResponse response, HttpSession session) {
        String username = "sczzdsf";
        String password = "@#001njcnpqs";
        HttpResult result;
        try {
            //在登录之前，判断当前session是否存在用户信息，存在便进行一次子系统登出（认证中心的session里的用户信息进行更新时，子系统便需要注销，重新认证）
            User ssoUser = TokenManager.getToken();
            if (null != ssoUser) {
                //注销当前会话中旧用户信息和权限资源
                logoutSystem(session.getId(), ssoUser.getUserIndex());
            }
            TokenManager.login(username, "", password, PubUtils.getIpAddr(request), ProjectEnum.SSO.getItem());
            TokenManager.getSession().setAttribute(KickoutSessionFilter.KICKOUT_STATUS, null);
            //保存登录记录
            userLoginService.insertLogin(username, ProjectEnum.SSO.getItem());
            onlineSession();
            result = PubUtils.assignmentResult(null, 200, "登录成功");
            userLogDetailService.saveUserLog(TokenManager.getToken().getUserIndex(), TokenManager.getToken().getLoginName(), "用户登录", "成功", appConfig.getSystemFlag(), PubUtils.getIpAddr(request), LogTypeEnum.SYSTEM.toString(), 0);
            //调整为重定向 todo...
            response.setStatus(203);
            String sessionId = TokenManager.getSession().getId().toString();
            TokenManager.getSession().setAttribute( CookieKeyEnum.SSOLOGIN.getCookieKey(), sessionId);
            //将认证后的session保存到cookie中
            saveTokenToCookie(request, response, sessionId);
            userUtil.saveRandomToken();
            WebUtils.redirectToSavedRequest(request, response, "/user/judgeLogin");
        } catch (Exception u) {
            logger.error("用户登录发生异常，异常为：" + u.toString());
            String exeString = u.getMessage();
            if (exeString.length() > 20) {
                exeString = "登录出错，联系管理员";
            }
            int level = 2;
            if (exeString.equalsIgnoreCase(UserStatusEnum.ERROR_ACCOUNT.getMessage())) {
                level = 1;
            }
            result = PubUtils.assignmentResult(null, 500, exeString);
            User user = new User();
            user.setLoginName(username);
            user = userMapper.selectOne(user);
            try {
                if (null == user) {
                    userLogDetailService.saveUserLog(username, username, exeString, "失败", appConfig.getSystemFlag(), PubUtils.getIpAddr(request), LogTypeEnum.SYSTEM.toString(), level);
                } else {
                    userLogDetailService.saveUserLog(user.getUserIndex(), user.getLoginName(), exeString, "失败", appConfig.getSystemFlag(), PubUtils.getIpAddr(request), LogTypeEnum.SYSTEM.toString(), level);
                }
            } catch (Exception e) {
                logger.error("用户异常登录日志入库出错，" + e.toString());
            }
        }
        return result;
    }


    /**
     * 校验验证码是否正确
     */
    private boolean judgeCode(String checkcode, HttpSession session) {
        boolean codeRight = false;
        // 校验图形验证码
        if (StringUtils.isNotBlank(checkcode)) {
            Object imgCode = session.getAttribute("capText");
            if ((imgCode != null) && (imgCode.toString().equalsIgnoreCase(checkcode))) {
                codeRight = true;
            }
        }
        //不管成功与否清空验证码的值
        session.setAttribute("capText", null);
        return codeRight;
    }

    /**
     * 处理密码_RSA
     */
    private String dealPasswordRSA(String password, String username, String ip) {
        byte[] value = jedisManager.getValueByKey(RedisDB.SHORT_TIME_QUERY, SerializeUtil.serialize(username + ip));
        RSAPrivateKey privateKey = SerializeUtil.deserialize(value, RSAPrivateKey.class);
        //秘钥用完即删
        jedisManager.deleteByKey(RedisDB.SHORT_TIME_QUERY, SerializeUtil.serialize(username + ip));
        password = PubUtils.getPasswordRSAVerify(privateKey, password);
        return password;
    }

    /**
     * 登录前进行会话最大并发量判断
     */
    private void judgeLoginSession(String username) {
        Collection<Session> allSessions = userShiroSessionRepository.getAllSessions();
        int count = 0;
        for (Session redisSession : allSessions) {
            if (null != redisSession) {
                //获取session登录信息。
                Object simplePrincipalCollectionObject = redisSession.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
                if (null != simplePrincipalCollectionObject) {
                    SimplePrincipalCollection spc = (SimplePrincipalCollection) simplePrincipalCollectionObject;
                    Object primaryPrincipalObject = spc.getPrimaryPrincipal();
                    if (primaryPrincipalObject instanceof User) {
                        User loginUser = (User) primaryPrincipalObject;
                        //相同登录名，不计数，否则计数到阀值时，无法挤掉相同用户，在异地登录
                        if (loginUser.getLoginName().equalsIgnoreCase(username)) {
                            continue;
                        }
                        SessionStatus sessionStatus = (SessionStatus) redisSession.getAttribute(UserSessionManager.SESSION_STATUS);
                        if (loginUser.getItem().equalsIgnoreCase("sso") & sessionStatus.isOnlineStatus()) {
                            count++;
                        }
                    }
                }
            }
        }
        //计数完毕后，查询系统的并发上限
        SysSet sysSet = new SysSet();
        sysSet.setState(1);
        try {
            sysSet = sysSetMapper.selectOne(sysSet);
            if (null == sysSet) {
                throw new AccountException("没有找到系统配置信息");
            } else {
                if (sysSet.getLimitNumber() < count) {
                    throw new AccountException("超过系统最大并发");
                }
            }
        } catch (Exception e) {
            throw new AccountException(e.getMessage());
        }
    }

    /**
     * 根据当前会话ID、用户ID清理子系统的会话，以及各个系统的用户权限资源
     */
    private void logoutSystem(String sessionId, String userIndex) {
        Session session = userShiroSessionRepository.getSession(sessionId);
        if (null == session) {
            return;
        }
        List<String> infoMap = (List<String>) session.getAttribute(sessionId + userIndex);
        if (!CollectionUtils.isEmpty(infoMap)) {
            //循环遍历删除redis中所有的session
            for (String id : infoMap) {
                userShiroSessionRepository.deleteSession(id);
            }
        }
        session.setAttribute(sessionId + userIndex, null);
        userShiroSessionRepository.activeSession(session);
        //同时需要清理所有系统下的角色以及资源信息
        springPubUtils.clearAllAuthor();
    }

    /**
     * 创建保存sso会话索引的cookie，以便子系统来认证
     *
     * @param request
     * @param sessionID 会话索引
     */
    private void saveTokenToCookie(HttpServletRequest request, HttpServletResponse response, String sessionID) {
        Cookie cookie = new Cookie("ssoToken", sessionID);
        cookie.setMaxAge(-1);
        cookie.setPath(request.getContextPath());
        response.addCookie(cookie);
    }


    /**
     * 携带项目标识重定向到sso登录页面
     *
     * @throws IOException
     */
    private void redirectLogin(HttpServletRequest request, HttpServletResponse response) throws IOException {
        //将sso的token置为空，登录成功后会重新赋值
        saveTokenToCookie(request, response, "");
        //重定向到登录页面
        WebUtils.issueRedirect(request, response, "/");
    }


    /**
     * 认证中心进行登出处理
     */
    @PostMapping("logOut")
    @ResponseBody
    public HttpResult logOut(HttpServletRequest request) {
        String tokenValue = PubUtils.getSSOToken(request);
        if (StringUtils.isNotBlank(tokenValue)) {
            logoutSession(tokenValue, request);
        }
        return PubUtils.assignmentResult(null, 200, "用户已经登出");
    }


    /**
     * 子系统注销用户时，在此处统一进行注销
     */
    @GetMapping("loginout")
    public void loginout(HttpServletRequest request, HttpServletResponse response) throws IOException {
        String tokenValue = PubUtils.getSSOToken(request);
        if (StringUtils.isNotBlank(tokenValue)) {
            logoutSession(tokenValue, request);
        }
        redirectLogin(request, response);
    }

    /**
     * 根据会话进行注销操作
     *
     * @param tokenValue
     * @param request
     */
    private void logoutSession(String tokenValue, HttpServletRequest request) {
        //根据tokenValue清除所有子系统的session
        Session session = userShiroSessionRepository.getSession(tokenValue);
        //根据会话进行注销操作
        if (null != session) {
            //根据session，将当前session置为失效
            invalidSession();
            Object obj = session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
            if (null != obj) {
                SimplePrincipalCollection spc = (SimplePrincipalCollection) obj;
                obj = spc.getPrimaryPrincipal();
                if (null != obj) {
                    User ssoUser = (User) obj;
                    try {
                        userLogDetailService.saveUserLog(ssoUser.getUserIndex(), ssoUser.getLoginName(), "用户退出", "成功", appConfig.getSystemFlag(), PubUtils.getIpAddr(request), LogTypeEnum.SYSTEM.toString(), 0);
                    } catch (javax.security.auth.login.AccountException e) {
                        logger.error("保存用户操作日志记录异常，操作为：用户退出,结果：失败,异常为：" + e.toString());
                    }
                    logoutSystem(tokenValue, ssoUser.getUserIndex());
                }
            }
        }
    }

    /**
     * 将会话置为失效
     */
    private void invalidSession( ) {
        Session session = userShiroSessionRepository.getSession(SecurityUtils.getSubject().getSession().getId());
        SessionStatus sessionStatus = new SessionStatus();
        sessionStatus.setOnlineStatus(false);
        session.setAttribute(UserSessionManager.SESSION_STATUS, sessionStatus);
        userShiroSessionRepository.saveSession(session);
    }

    /**
     * 将会话置为在线
     */
    private void onlineSession() {
        Session session = userShiroSessionRepository.getSession(SecurityUtils.getSubject().getSession().getId());
        SessionStatus sessionStatus = new SessionStatus();
        sessionStatus.setOnlineStatus(true);
        session.setAttribute(UserSessionManager.SESSION_STATUS, sessionStatus);
        userShiroSessionRepository.saveSession(session);
    }


}
