cas+shiro 的单用户登录

本文介绍了一个使用CAS和Shiro实现的单用户登录系统,当同一用户在多个设备上登录时,系统会强制登出先前的会话。通过调用CAS服务器的接口获取SSO会话并删除旧的ticket,确保最新登录的会话有效。详细步骤包括关注JSESSIONID、ticket_granting_ticket和authenticated_services参数,并提供了服务器和客户端的代码修改建议。

该系统分为两部分,服务器和客户端,这里要说明一下,服务器上删除ticket的代码,我是拿的悟空大神的,并没有改动,具体地址为:https://blog.youkuaiyun.com/u010475041/article/details/78662266 

应用场景:

A登陆了系统,正在玩着玩着,B也登陆了系统。这时候让A被迫下线。

思路:

首先我们先知道,在cas服务端,是有方法可以获取服务端现在的ticket的,找到那个方法。我的地址是: https://你的域名或者ip:8443(接口)/cas(这个你不一定需要)/status/ssosessions/getSsoSessions。总之,方法地址就是/status/ssosessions/getSsoSessions这里了。

我拿谷歌和火狐两个浏览器,测试这个接口 ,分别的返回值如下:

先登入谷歌:
cookie  JSESSIONID  288854BD53C476D66436476AEEDE7175
{"totalUsageCount":2,"activeSsoSessions":[{"authentication_date":1551920722.969000000,"authentication_date_formatted":"2019-03-07T09:05:22Z","authentication_attributes":{"authenticationMethod":"QueryAndEncodeDatabaseAuthenticationHandler","successfulAuthenticationHandlers":["QueryAndEncodeDatabaseAuthenticationHandler"]},"authenticated_principal":"admin","number_of_uses":2,
"ticket_granting_ticket":"TGT-1-lwfjdiF2qR92VLaoicY6vesdSrngHkGIeqpD1LGrmuhUphONof-PC-20171207BQO","principal_attributes":{"email":"admin@163.com","id":"813487c718f645f483226acac6edc284","intime":1531880261000,"password":"c41d7c66e1b8404545aa3a0ece2006ac","username":"admin"},
"is_proxied":false,
"authenticated_services":{"ST-1-gE50uqUyMCMRzsdmoWst-PC-20171207BQO":{"id":"http://localhost:8084/callback?client_name=cas","originalUrl":"http://localhost:8084/callback?client_name=cas","artifactId":null,"principal":{"@class":"org.apereo.cas.authentication.principal.SimplePrincipal","id":"admin","attributes":{"email":"admin@163.com","id":"813487c718f645f483226acac6edc284","intime":1531880261000,"password":"c41d7c66e1b8404545aa3a0ece2006ac","username":"admin"}},"loggedOutAlready":false}}}],"totalTicketGrantingTickets":1,"totalTickets":1,"totalPrincipals":1,"totalProxyGrantingTickets":0}
接着火狐:
cookie  JSESSIONID  7D5B6137FE232A820A450D45CEEE1ACD
{"totalUsageCount":2,"activeSsoSessions":[{"authentication_date":1551921028.080000000,"authentication_date_formatted":"2019-03-07T09:10:28Z","authentication_attributes":{"authenticationMethod":"QueryAndEncodeDatabaseAuthenticationHandler","successfulAuthenticationHandlers":["QueryAndEncodeDatabaseAuthenticationHandler"]},"authenticated_principal":"admin","number_of_uses":2,
"ticket_granting_ticket":"TGT-2-RtfJrwAkUeJQkpkOHdIt0h5GsgiZ7cqpofETqMCyTG0fjZRFbZ-PC-20171207BQO","principal_attributes":{"email":"admin@163.com","id":"813487c718f645f483226acac6edc284","intime":1531880261000,"password":"c41d7c66e1b8404545aa3a0ece2006ac","username":"admin"},
"is_proxied":false,
"authenticated_services":{"ST-2-9C1CRuDGydocDMsQsMEJ-PC-20171207BQO":{"id":"http://localhost:8084/callback?client_name=cas","originalUrl":"http://localhost:8084/callback?client_name=cas","artifactId":null,"principal":{"@class":"org.apereo.cas.authentication.principal.SimplePrincipal","id":"admin","attributes":{"email":"admin@163.com","id":"813487c718f645f483226acac6edc284","intime":1531880261000,"password":"c41d7c66e1b8404545aa3a0ece2006ac","username":"admin"}},"loggedOutAlready":false}}}],"totalTicketGrantingTickets":1,"totalTickets":1,"totalPrincipals":1,"totalProxyGrantingTickets":0}
刷新谷歌之后的:
{"totalUsageCount":2,"activeSsoSessions":[{"authentication_date":1551921028.080000000,"authentication_date_formatted":"2019-03-07T09:10:28Z","authentication_attributes":{"authenticationMethod":"QueryAndEncodeDatabaseAuthenticationHandler","successfulAuthenticationHandlers":["QueryAndEncodeDatabaseAuthenticationHandler"]},"authenticated_principal":"admin","number_of_uses":2,
"ticket_granting_ticket":"TGT-2-RtfJrwAkUeJQkpkOHdIt0h5GsgiZ7cqpofETqMCyTG0fjZRFbZ-PC-20171207BQO","principal_attributes":{"email":"admin@163.com","id":"813487c718f645f483226acac6edc284","intime":1531880261000,"password":"c41d7c66e1b8404545aa3a0ece2006ac","username":"admin"},
"is_proxied":false,
"authenticated_services":{"ST-2-9C1CRuDGydocDMsQsMEJ-PC-20171207BQO":{"id":"http://localhost:8084/callback?client_name=cas","originalUrl":"http://localhost:8084/callback?client_name=cas","artifactId":null,"principal":{"@class":"org.apereo.cas.authentication.principal.SimplePrincipal","id":"admin","attributes":{"email":"admin@163.com","id":"813487c718f645f483226acac6edc284","intime":1531880261000,"password":"c41d7c66e1b8404545aa3a0ece2006ac","username":"admin"}},"loggedOutAlready":false}}}],"totalTicketGrantingTickets":1,"totalTickets":1,"totalPrincipals":1,"totalProxyGrantingTickets":0}


看着一堆,其实你只需要注意-----JSESSIONID ,ticket_granting_ticket,authenticated_services,这三个参数就够了。

这是每次登入不通浏览器时,服务器保存的值。

这时候你就要知道了,你要修改一下服务器的代码,告诉服务器,同一个账号在两个地方登陆,只保留最后一次的值。也就是说,把之前的值删了,只保留最新的值。

之后,你的客户端去访问时,才可以获取到最新的值,再和你保存在自己本地的cookie进行比较,看看是不是一样的,有没有被人挤下去。

 

代码:

先看服务器的代码:

其实可以直接去下载大神的包,拿来就行了。

1.引入相应的包()

<?xml version="1.0" encoding="UTF-8"?>
<!--
  ~ 版权所有.(c)2008-2017. 卡尔科技工作室
  -->

<!--
  ~ 版权所有.(c)2008-2017. 卡尔科技工作室
  -->

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>sso-support</artifactId>
        <groupId>com.carl.auth</groupId>
        <version>1.7.0-SNAPSHOT</version>
        <relativePath>../pom.xml</relativePath>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>sso-support-single-login</artifactId>


    <dependencies>
        <dependency>
            <groupId>org.apereo.cas</groupId>
            <artifactId>cas-server-core-configuration</artifactId>
            <version>${cas.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apereo.cas</groupId>
            <artifactId>cas-server-core-audit</artifactId>
            <version>${cas.version}</version>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.apereo.cas</groupId>
            <artifactId>cas-server-core</artifactId>
            <version>${cas.version}</version>
        </dependency>
    </dependencies>
</project>

 

2.创建java类:

/*
 * 版权所有.(c)2008-2017. 卡尔科技工作室
 */


package com.carl.sso.support.single.config;

import com.carl.sso.support.single.listener.TGTCreateEventListener;
import com.carl.sso.support.single.service.TriggerLogoutService;
import com.carl.sso.support.single.service.UserIdObtainServiceImpl;
import org.apereo.cas.CentralAuthenticationService;
import org.apereo.cas.configuration.CasConfigurationProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 登出配置
 *
 * @author Carl
 * @date 2017/11/29
 */
@Configuration("singleLogoutTriggerConfiguration")
@EnableConfigurationProperties(CasConfigurationProperties.class)
public class SingleLogoutTriggerConfiguration {
    @Autowired
    private CentralAuthenticationService centralAuthenticationService;

    /**
     * 触发登出服务
     *
     * @return 触发登出服务
     */
    @Bean
    protected TriggerLogoutService triggerLogoutService() {
        return new TriggerLogoutService(centralAuthenticationService);
    }

    @Bean
    //注册事件监听tgt的创建
    protected TGTCreateEventListener tgtCreateEventListener() {
        TGTCreateEventListener listener = new TGTCreateEventListener(triggerLogoutService(), new UserIdObtainServiceImpl());
        return listener;
    }
}

 

/*
 * 版权所有.(c)2008-2017. 卡尔科技工作室
 */

package com.carl.sso.support.single.listener;

import com.carl.sso.support.single.service.IUserIdObtainService;
import com.carl.sso.support.single.service.TriggerLogoutService;
import org.apereo.cas.support.events.ticket.CasTicketGrantingTicketCreatedEvent;
import org.apereo.cas.ticket.TicketGrantingTicket;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;

import javax.validation.constraints.NotNull;
import java.util.List;

/**
 * 识别事件然后删除
 *
 * @author Carl
 * @version 创建时间:2017/11/29
 */
public class TGTCreateEventListener {
    private TriggerLogoutService logoutService;
    private IUserIdObtainService service;

    public TGTCreateEventListener(@NotNull TriggerLogoutService logoutService, @NotNull IUserIdObtainService service) {
        this.logoutService = logoutService;
        this.service = service;
    }

    @EventListener
    @Async
    public void onTgtCreateEvent(CasTicketGrantingTicketCreatedEvent event) {
        TicketGrantingTicket ticketGrantingTicket = event.getTicketGrantingTicket();
        String id = ticketGrantingTicket.getAuthentication().getPrincipal().getId();
        String tgt = ticketGrantingTicket.getId();
        String clientName = (String) ticketGrantingTicket.getAuthentication().getAttributes().get("clientName");
        //获取可以认证的id
        List<String> authIds = service.obtain(clientName, id);
        if (authIds != null) {
            //循环触发登出
            authIds.forEach(authId -> logoutService.triggerLogout(authId, tgt));

        }
    }
}
/*
 * 版权所有.(c)2008-2017. 卡尔科技工作室
 */
package com.carl.sso.support.single.service;

import java.util.List;

/**
 * 用户认证识别器
 *
 * @author Carl
 * @version 创建时间:2017/11/29
 */
public interface IUserIdObtainService {

    /**
     * 通过登录方式查询其他的id
     *
     * @param clientName 登录方式
     * @param id         用户id
     * @return 所有用户id
     */
    List<String> obtain(String clientName, String id);
}
/*
 * 版权所有.(c)2008-2017. 卡尔科技工作室
 */



package com.carl.sso.support.single.service;

import org.apereo.cas.CentralAuthenticationService;
import org.apereo.cas.authentication.Authentication;
import org.apereo.cas.ticket.Ticket;
import org.apereo.cas.ticket.TicketGrantingTicket;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Collection;

/**
 * 登出触发器
 *
 * @author Carl
 * @date 2017/11/29
 */
public class TriggerLogoutService {
    private static final Logger LOGGER = LoggerFactory.getLogger(TriggerLogoutService.class);
    private CentralAuthenticationService service;

    public TriggerLogoutService(CentralAuthenticationService service) {
        this.service = service;
    }

    /**
     * 触发其他用户退出
     *
     * @param id  用户id
     * @param tgt 当前登录的tgt
     */
    public void triggerLogout(String id, String tgt) {
        //找出用户id,并且不为当前tgt的,这里应当考虑数据性能,直接筛选用户再筛选tgt

        Collection<Ticket> tickets = this.service.getTickets(ticket -> {
            if(ticket instanceof TicketGrantingTicket) {
                TicketGrantingTicket t = ((TicketGrantingTicket)ticket).getRoot();
                Authentication authentication = t.getAuthentication();
                return t != null && authentication != null
                        && authentication.getPrincipal() != null && id.equals(authentication.getPrincipal().getId())
                        && !tgt.equals(t.getId());
            } else {
                return false;
            }

        });

        if (tickets != null && tickets.size() > 0) {
            LOGGER.error(String.format("[%s]强制强制注销%s", id, tickets.size()));

        }

        //发出注销
        for (Ticket ticket : tickets) {
            service.destroyTicketGrantingTicket(ticket.getId());


        }
    }
}

 

 

/*
 * 版权所有.(c)2008-2017. 卡尔科技工作室
 */

package com.carl.sso.support.single.service;


import java.util.ArrayList;
import java.util.List;

/**
 * @author Carl
 * @version 创建时间:2017/11/29
 */
public class UserIdObtainServiceImpl implements IUserIdObtainService {

    public UserIdObtainServiceImpl() {

    }

    @Override
    public List<String> obtain(String clientName, String id) {
        //由于这里目前只做测试所以只返回当前的id,在正常的情况逻辑应该如下

        //根据校验client以及登录的id找到其他同一个用户的所有校验id返回,如通过邮箱登录的id,通过github登录的id等等
        List<String> ids = new ArrayList<>();
        ids.add(id);
        return ids;
    }
}

 

配置文件:

#记住我
cas.ticket.tgt.rememberMe.enabled=true
cas.ticket.tgt.rememberMe.timeToKillInSeconds=43200
# 监控系统
spring.boot.admin.url=http://localhost:8444
spring.boot.admin.client.managementUrl=http://localhost:8443/cas/status

这在原有的sso-server上加上

<dependency>
            <groupId>com.carl.auth</groupId>
            <artifactId>sso-support-custom-auth</artifactId>
            <version>${project.version}</version>
        </dependency>
        <!--单用户登录-->
        <dependency>
            <groupId>com.carl.auth</groupId>
            <artifactId>sso-support-single-login</artifactId>
            <version>${project.version}</version>
        </dependency>
        <dependency>
            <groupId>com.carl.auth</groupId>
            <artifactId>sso-support-captcha</artifactId>
            <version>${project.version}</version>
        </dependency>

 

 接着是客户端代码:

拦截器

package com.mlcp.config;

import ch.qos.logback.core.net.SyslogOutputStream;
import com.mlcp.org.jasig.cas.client.session.HashMapBackedSessionMappingStorage;
import com.mlcp.org.jasig.cas.client.session.SessionMappingStorage;
import com.mlcp.org.jasig.cas.client.session.SingleSignOutFilter;
import com.mlcp.org.jasig.cas.client.util.CommonUtils;
import com.mlcp.org.pac4j.cas.config.CasConfiguration;
import com.mlcp.org.pac4j.cas.util.HttpUtils;
import com.mlcp.sso.CasJsonUtil;
import io.buji.pac4j.context.ShiroSessionStore;
import com.mlcp.org.pac4j.cas.client.rest.CasRestFormClient;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.session.Session;
import org.apache.shiro.util.ByteSource;
import org.pac4j.cas.credentials.extractor.TicketAndLogoutRequestExtractor;
import com.mlcp.org.pac4j.cas.profile.CasRestProfile;
import org.apache.commons.lang.StringUtils;
import org.apache.shiro.authz.UnauthorizedException;
import org.hibernate.annotations.Filters;
import org.pac4j.core.context.*;
import org.pac4j.core.credentials.TokenCredentials;
import org.pac4j.core.exception.TechnicalException;
import org.pac4j.core.profile.ProfileManager;
import org.pac4j.core.store.GuavaStore;
import org.pac4j.core.store.Store;
import org.pac4j.core.util.CommonHelper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;


import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.*;
import java.lang.reflect.Method;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.Principal;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

@Configuration
public class AuthInterceptor implements HandlerInterceptor {

    @Value("${cas.serviceUrl}")
    private String serviceUrl;
    @Value("${cas.getSessionUrl}")
    private String getSessionUrl;

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object obj) throws Exception {



            boolean flag = false;
            Principal principal = request.getUserPrincipal();

            if(principal!=null&&principal.getName()!=null&&!principal.getName().equals("")){
                flag = SSOService(request,response,principal.getName());
                if(!flag){
                    return flag;
                }
            }

            String url = request.getRequestURL().toString();
            if (!flag)
            {
                Integer status = response.getStatus();

                if (null != status && StringUtils.isNotEmpty(status.toString()) && status == 200)
                {
                    flag = true;
                } else {
                    //logger.error("token is invalidate :" + request.getRemoteHost() + "  url  :" + request.getContextPath());
                    response.setHeader("sessionstatus", "timeout");
                    flag = false;
                    return flag;
                }
            }



            return flag;




        }


    public static boolean isAjaxRequest(HttpServletRequest request) {
        String header = request.getHeader("X-Requested-With");
        if (header != null && "XMLHttpRequest".equals(header))
            return true;
        else
            return false;
    }

    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse res, Object o,
                           ModelAndView modelAndView) throws Exception {
    }

    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
                                Object o, Exception e) throws Exception {


    }

    /**
     * 检查是否是单用户登陆
     * @param request
     * @param response
     * @param userName
     * @return
     * @throws IOException
     */
    private boolean SSOService(HttpServletRequest request, HttpServletResponse response,String userName) throws IOException {
        HttpClient httpclient = new HttpClient();
        GetMethod httpget = new GetMethod(getSessionUrl);
        try {
            int code=httpclient.executeMethod(httpget);
            if(code==200) {
                String session = httpget.getResponseBodyAsString();
                String ticket_Original = CasJsonUtil.getTicket_granting_ticket(session,userName);
                if(ticket_Original.equals("")){
                    response.setHeader("sessionstatus", "singleSignUser");
                    return  false;
                }


                ByteSource credentialsSalt = ByteSource.Util.bytes(userName);
                String ticket_Transformation = new SimpleHash("MD5", ticket_Original,
                        credentialsSalt, 1024).toString();

                Cookie[] cookies= request.getCookies();
                String jsessionId="";
                String cookies_Original="";
                for(Cookie cookie:cookies){
                    if(cookie.getName().equals("JSESSIONID")){
                        jsessionId=cookie.getValue();
                    }

                    //判断cookie里有没有ticket,如果有,则判断存储的ticket与现在传递过来的ticket是否想等
                    //若想等,则返回true,不等,则清空ticket并且创建three-ticket来记录jsessionid的值,该值是为了比较该用户是否为第一次登陆。
                    //注意:在刷新页面时会有多次走进拦截器,所以单纯的将mlcp-madnet-ticket删除是没用的,他还会再创建一个。
                    if(cookie.getName().equals("mlcp-madnet-ticket-"+userName)){
                        cookies_Original=cookie.getValue();
                       if(cookies_Original.equals(ticket_Transformation)){
                            return true;
                        }else {
                           Cookie threeTicket = new Cookie("three-ticket-" + userName, jsessionId);
                           threeTicket.setPath("/");
                           threeTicket.setMaxAge(43200);
                            response.addCookie(threeTicket);
                            cookie.setValue("");
                            cookie.setMaxAge(0);
                            cookie.setPath("/");
                            response.addCookie(cookie);
                            response.setHeader("sessionstatus", "singleSignUser");
                            return false;
                        }

                    }


                }


                   Cookie[] cookiesSave= request.getCookies();
                   String jsessionCompar="";
                   for(Cookie cookie:cookiesSave){

                       if(cookie.getName().equals("JSESSIONID")){
                           jsessionCompar=cookie.getValue();
                       }

                       //若有这个值并且该值等于jsessionid的值,则证明该用户被挤下去了
                       if(cookie.getName().equals("three-ticket-" + userName)){
                           String threeValue=cookie.getValue();
                           if(threeValue.equals(jsessionCompar)){
                               return false;
                           }
                       }
                }

                //经过上面的一系列条件判断,到这里可以确定该用户为首次登陆,创建ticket
                Cookie saveTicket = new Cookie("mlcp-madnet-ticket-" + userName, ticket_Transformation);
                saveTicket.setPath("/");
                saveTicket.setMaxAge(43200);
                response.addCookie(saveTicket);

            }
        } finally {
            httpget.releaseConnection();
        }

        return true;
    }


}
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.apache.shiro.crypto.hash;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.codec.CodecException;
import org.apache.shiro.codec.Hex;
import org.apache.shiro.crypto.UnknownAlgorithmException;
import org.apache.shiro.util.ByteSource;
import org.apache.shiro.util.StringUtils;
import org.apache.shiro.util.ByteSource.Util;

public class SimpleHash extends AbstractHash {
    private static final int DEFAULT_ITERATIONS = 1;
    private final String algorithmName;
    private byte[] bytes;
    private ByteSource salt;
    private int iterations;
    private transient String hexEncoded;
    private transient String base64Encoded;

    public SimpleHash(String algorithmName) {
        this.hexEncoded = null;
        this.base64Encoded = null;
        this.algorithmName = algorithmName;
        this.iterations = 1;
    }

    public SimpleHash(String algorithmName, Object source) throws CodecException, UnknownAlgorithmException {
        this(algorithmName, source, (Object)null, 1);
    }

    public SimpleHash(String algorithmName, Object source, Object salt) throws CodecException, UnknownAlgorithmException {
        this(algorithmName, source, salt, 1);
    }

    public SimpleHash(String algorithmName, Object source, Object salt, int hashIterations) throws CodecException, UnknownAlgorithmException {
        this.hexEncoded = null;
        this.base64Encoded = null;
        if (!StringUtils.hasText(algorithmName)) {
            throw new NullPointerException("algorithmName argument cannot be null or empty.");
        } else {
            this.algorithmName = algorithmName;
            this.iterations = Math.max(1, hashIterations);
            ByteSource saltBytes = null;
            if (salt != null) {
                saltBytes = this.convertSaltToBytes(salt);
                this.salt = saltBytes;
            }

            ByteSource sourceBytes = this.convertSourceToBytes(source);
            this.hash(sourceBytes, saltBytes, hashIterations);
        }
    }

    protected ByteSource convertSourceToBytes(Object source) {
        return this.toByteSource(source);
    }

    protected ByteSource convertSaltToBytes(Object salt) {
        return this.toByteSource(salt);
    }

    protected ByteSource toByteSource(Object o) {
        if (o == null) {
            return null;
        } else if (o instanceof ByteSource) {
            return (ByteSource)o;
        } else {
            byte[] bytes = this.toBytes(o);
            return Util.bytes(bytes);
        }
    }

    private void hash(ByteSource source, ByteSource salt, int hashIterations) throws CodecException, UnknownAlgorithmException {
        byte[] saltBytes = salt != null ? salt.getBytes() : null;
        byte[] hashedBytes = this.hash(source.getBytes(), saltBytes, hashIterations);
        this.setBytes(hashedBytes);
    }

    public String getAlgorithmName() {
        return this.algorithmName;
    }

    public ByteSource getSalt() {
        return this.salt;
    }

    public int getIterations() {
        return this.iterations;
    }

    public byte[] getBytes() {
        return this.bytes;
    }

    public void setBytes(byte[] alreadyHashedBytes) {
        this.bytes = alreadyHashedBytes;
        this.hexEncoded = null;
        this.base64Encoded = null;
    }

    public void setIterations(int iterations) {
        this.iterations = Math.max(1, iterations);
    }

    public void setSalt(ByteSource salt) {
        this.salt = salt;
    }

    protected MessageDigest getDigest(String algorithmName) throws UnknownAlgorithmException {
        try {
            return MessageDigest.getInstance(algorithmName);
        } catch (NoSuchAlgorithmException var4) {
            String msg = "No native '" + algorithmName + "' MessageDigest instance available on the current JVM.";
            throw new UnknownAlgorithmException(msg, var4);
        }
    }

    protected byte[] hash(byte[] bytes) throws UnknownAlgorithmException {
        return this.hash((byte[])bytes, (byte[])null, 1);
    }

    protected byte[] hash(byte[] bytes, byte[] salt) throws UnknownAlgorithmException {
        return this.hash((byte[])bytes, (byte[])salt, 1);
    }

    protected byte[] hash(byte[] bytes, byte[] salt, int hashIterations) throws UnknownAlgorithmException {
        MessageDigest digest = this.getDigest(this.getAlgorithmName());
        if (salt != null) {
            digest.reset();
            digest.update(salt);
        }

        byte[] hashed = digest.digest(bytes);
        int iterations = hashIterations - 1;

        for(int i = 0; i < iterations; ++i) {
            digest.reset();
            hashed = digest.digest(hashed);
        }

        return hashed;
    }

    public boolean isEmpty() {
        return this.bytes == null || this.bytes.length == 0;
    }

    public String toHex() {
        if (this.hexEncoded == null) {
            this.hexEncoded = Hex.encodeToString(this.getBytes());
        }

        return this.hexEncoded;
    }

    public String toBase64() {
        if (this.base64Encoded == null) {
            this.base64Encoded = Base64.encodeToString(this.getBytes());
        }

        return this.base64Encoded;
    }

    public String toString() {
        return this.toHex();
    }

    public boolean equals(Object o) {
        if (o instanceof Hash) {
            Hash other = (Hash)o;
            return MessageDigest.isEqual(this.getBytes(), other.getBytes());
        } else {
            return false;
        }
    }

    public int hashCode() {
        return this.bytes != null && this.bytes.length != 0 ? Arrays.hashCode(this.bytes) : 0;
    }
}

 

前台:

$.ajaxSetup({
    complete: function (xhr, status) {
        var win = window;
        var sessionStatus = xhr.getResponseHeader('sessionstatus'); // 通过XMLHttpRequest取得响应头,sessionstatus,
        if (sessionStatus == "timeout") {
            // 如果超时就处理 ,指定要跳转的页面
            //toastr.error("您的认证已过期,将为您刷新页面");
            //window.location.href = "http://localhost:8084";
            //var test = window.location.href;
            //  window.location.href= "http://localhost:8888/config/a.html?service="+test;
            Ewin.confirm({message: "您的认证已过期,将为您刷新页面"}).on(function (a) {

                if (!a) {

                    return;
                } else {
                    win.location.href = win.location.href;
                    return;
                }

            })}
        if (sessionStatus == "singleSignUser") {
            // 如果超时就处理 ,指定要跳转的页面
            //toastr.error("您的认证已过期,将为您刷新页面");
            //window.location.href = "http://localhost:8084";
            //var test = window.location.href;
            //  window.location.href= "http://localhost:8888/config/a.html?service="+test;
            Ewin.confirm({message: "该账号在其他地方登陆,您被迫下线"}).on(function (a) {

                if (!a) {

                    return;
                } else {
                    var exp = new Date();
                    exp.setTime(exp.getTime() - 1);
                        // 这里删除操作其实是将expires过期时间设置为当前时间,使cookie立即过期
                    win.location.href = "/logout?service=http://223.68.192.125:8084";
                    return;
                }

            })}
        if(xhr.status == 401){

            while (win != win.top) {
                win = win.top;
            }
            //重新跳转到 login.html
            win.location.href = win.location.href;
            return;
        }
    }
})

拦截器加到项目里:

不需要的自己删掉

package com.mlcp.config;

import java.io.IOException;
import java.util.List;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.Ordered;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;

@Configuration
public class CustomWebMvcConfigurerAdapter extends WebMvcConfigurerAdapter {


    @Value("${site.uploadPath}")
    private String uploadPath;
    @Value("${site.mlcpStaticUrl}")
    private String mlcpStaticUrl;

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        //registry.addViewController("/").setViewName("redirect:/login/login.html");

        //registry.addViewController("/").setViewName("redirect:/main/newMain.html");
        //registry.setOrder(Ordered.HIGHEST_PRECEDENCE);
        //super.addViewControllers(registry);
    }

    @Bean
    AuthInterceptor authInterceptor() {
        return new AuthInterceptor();
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //registry.addInterceptor(new AuthInterceptor()).addPathPatterns("/**").excludePathPatterns("/login").excludePathPatterns("/register");
        registry.addInterceptor(authInterceptor());
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {

        registry.addResourceHandler(mlcpStaticUrl + "**").addResourceLocations("file:" + uploadPath);

        super.addResourceHandlers(registry);
    }

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        super.configureMessageConverters(converters);
        converters.add(new MappingJackson2HttpMessageConverter());
    }

    @Bean
    @Primary
    @ConditionalOnMissingBean(ObjectMapper.class)
    public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
        ObjectMapper objectMapper = builder.createXmlMapper(false).build();

        /*
         * 返回的JSON字符串中含有我们并不需要的字段,那么当对应的实体类中不含有该字段时,会抛出一个异常,告诉你有些字段没有在实体类中找到
         */
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

        SerializerProvider serializerProvider = objectMapper.getSerializerProvider();

        // 允许单引号
        //objectMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
        // 字段和值都加引号
        //objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
        // 数字也加引号
        //objectMapper.configure(JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS, true);
        //objectMapper.configure(JsonGenerator.Feature.QUOTE_NON_NUMERIC_NUMBERS, true);

        //Null值输出空字符串
        serializerProvider.setNullValueSerializer(new JsonSerializer<Object>() {
            @Override
            public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
                jsonGenerator.writeString("");
            }

        });

        return objectMapper;
    }


}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

那个id是什么东西

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值