diff --git a/README.md b/README.md index 4073fd0..26e77ca 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,7 @@ * 验证码发送、校验(注册发送邮箱验证码) * 服务监控检测 * 校验码登录 +* 单用户登录 ## Tutorial: [![Csdn Blog Tutorial](https://img.shields.io/badge/csdn%20blog-tutorial-orange.svg)](http://blog.csdn.net/u010475041/article/category/7156505) diff --git a/sso-server/pom.xml b/sso-server/pom.xml index 304f892..b4fe52b 100644 --- a/sso-server/pom.xml +++ b/sso-server/pom.xml @@ -32,6 +32,12 @@ sso-support-custom-auth ${project.version} + + + com.carl.auth + sso-support-single-login + ${project.version} + com.carl.auth sso-support-captcha diff --git a/sso-support/pom.xml b/sso-support/pom.xml index 9d18b25..a45cd3e 100644 --- a/sso-support/pom.xml +++ b/sso-support/pom.xml @@ -19,6 +19,7 @@ sso-support-custom-auth sso-support-captcha sso-support-validate + sso-support-single-login diff --git a/sso-support/sso-support-single-login/README.md b/sso-support/sso-support-single-login/README.md new file mode 100644 index 0000000..3135444 --- /dev/null +++ b/sso-support/sso-support-single-login/README.md @@ -0,0 +1 @@ +单用户登录,用户踢出 \ No newline at end of file diff --git a/sso-support/sso-support-single-login/pom.xml b/sso-support/sso-support-single-login/pom.xml new file mode 100644 index 0000000..d24aedf --- /dev/null +++ b/sso-support/sso-support-single-login/pom.xml @@ -0,0 +1,42 @@ + + + + + + + + sso-support + com.carl.auth + 1.7.0-SNAPSHOT + ../pom.xml + + 4.0.0 + + sso-support-single-login + + + + + org.apereo.cas + cas-server-core-configuration + ${cas.version} + + + org.apereo.cas + cas-server-core-audit + ${cas.version} + true + + + org.apereo.cas + cas-server-core + ${cas.version} + + + \ No newline at end of file diff --git a/sso-support/sso-support-single-login/src/main/java/com/carl/sso/support/single/config/SingleLogoutTriggerConfiguration.java b/sso-support/sso-support-single-login/src/main/java/com/carl/sso/support/single/config/SingleLogoutTriggerConfiguration.java new file mode 100644 index 0000000..27c6a61 --- /dev/null +++ b/sso-support/sso-support-single-login/src/main/java/com/carl/sso/support/single/config/SingleLogoutTriggerConfiguration.java @@ -0,0 +1,46 @@ +/* + * 版权所有.(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; + } +} diff --git a/sso-support/sso-support-single-login/src/main/java/com/carl/sso/support/single/listener/TGTCreateEventListener.java b/sso-support/sso-support-single-login/src/main/java/com/carl/sso/support/single/listener/TGTCreateEventListener.java new file mode 100644 index 0000000..80169fa --- /dev/null +++ b/sso-support/sso-support-single-login/src/main/java/com/carl/sso/support/single/listener/TGTCreateEventListener.java @@ -0,0 +1,46 @@ +/* + * 版权所有.(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 authIds = service.obtain(clientName, id); + if (authIds != null) { + //循环触发登出 + authIds.forEach(authId -> logoutService.triggerLogout(authId, tgt)); + } + } +} diff --git a/sso-support/sso-support-single-login/src/main/java/com/carl/sso/support/single/service/IUserIdObtainService.java b/sso-support/sso-support-single-login/src/main/java/com/carl/sso/support/single/service/IUserIdObtainService.java new file mode 100644 index 0000000..b6cea02 --- /dev/null +++ b/sso-support/sso-support-single-login/src/main/java/com/carl/sso/support/single/service/IUserIdObtainService.java @@ -0,0 +1,24 @@ +/* + * 版权所有.(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 obtain(String clientName, String id); +} diff --git a/sso-support/sso-support-single-login/src/main/java/com/carl/sso/support/single/service/TriggerLogoutService.java b/sso-support/sso-support-single-login/src/main/java/com/carl/sso/support/single/service/TriggerLogoutService.java new file mode 100644 index 0000000..267b104 --- /dev/null +++ b/sso-support/sso-support-single-login/src/main/java/com/carl/sso/support/single/service/TriggerLogoutService.java @@ -0,0 +1,62 @@ +/* + * 版权所有.(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 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.info(String.format("[%s]强制强制注销%s", id, tickets.size())); + } + + //发出注销 + for (Ticket ticket : tickets) { + service.destroyTicketGrantingTicket(ticket.getId()); + } + } +} diff --git a/sso-support/sso-support-single-login/src/main/java/com/carl/sso/support/single/service/UserIdObtainServiceImpl.java b/sso-support/sso-support-single-login/src/main/java/com/carl/sso/support/single/service/UserIdObtainServiceImpl.java new file mode 100644 index 0000000..4079c96 --- /dev/null +++ b/sso-support/sso-support-single-login/src/main/java/com/carl/sso/support/single/service/UserIdObtainServiceImpl.java @@ -0,0 +1,30 @@ +/* + * 版权所有.(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 obtain(String clientName, String id) { + //由于这里目前只做测试所以只返回当前的id,在正常的情况逻辑应该如下 + + //根据校验client以及登录的id找到其他同一个用户的所有校验id返回,如通过邮箱登录的id,通过github登录的id等等 + List ids = new ArrayList<>(); + ids.add(id); + return ids; + } +} diff --git a/sso-support/sso-support-single-login/src/main/resources/META-INF/spring.factories b/sso-support/sso-support-single-login/src/main/resources/META-INF/spring.factories new file mode 100644 index 0000000..77948a3 --- /dev/null +++ b/sso-support/sso-support-single-login/src/main/resources/META-INF/spring.factories @@ -0,0 +1 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.carl.sso.support.single.config.SingleLogoutTriggerConfiguration \ No newline at end of file