该系统分为两部分,服务器和客户端,这里要说明一下,服务器上删除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;
}
}