在前面的文章《实战Spring Cloud Oauth2系列(五)实现多客户端登录》我们介绍了实现多客户端登录的功能,在实际的场景里面我们应该有见识过,例如微信,他可以同时支持手机端登录和电脑端登录,这就是同一个账号实现多端登录的原理。但是我们还会发现一个场景,就是微信在A手机上登录了之后,再去B手机上登录,此时B手机上登录成功之后,A手机上的登录就会被挤掉。这也就是我们这篇文章的核心主题-实现单端登录并且踢掉老登录的效果。
其实实现这个单端登录并且踢掉老登录的流程非常简单,我们画个流程图:
所以说这里我们了解了这个流程之后,就知道应该怎么办了,接下来我们直接实操,这里我们还是更改之前的OauthController代码。
下面是OauthController的完整代码:
package org.shop.oauth.server.controller; import java.security.Principal; import java.util.Map; import org.shop.common.model.http.BaseResponse; import org.shop.oauth.server.service.AccessTokenService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import com.alibaba.fastjson.JSON; import lombok.extern.slf4j.Slf4j; @RestController @RequestMapping("/oauth") @Slf4j public class OauthController { @Autowired private AccessTokenService accessTokenService; /** * 重写/oauth/token这个默认接口,返回的数据格式统一 * @throws Exception */ @PostMapping(value = "/token") public BaseResponse postAccessToken(Principal principal, @RequestParam Map<String, String> parameters) throws Exception { //备注,在这里的话,我们能判断出来用户账户密码错误,用户状态,权限等信息。在这里判断的话,就不需要唉service中抛出具体的错误信息了。 log.info("登录获取到的传递参数是:{}",JSON.toJSONString(parameters)); return accessTokenService.createNewToken(principal,parameters); } /** * 重写/oauth/token这个默认接口,返回的数据格式统一 * @throws Exception */ @PostMapping(value = "/logout") public BaseResponse logout(@RequestHeader String accessToken) throws Exception { log.info("用户退出接口的传递参数是:{}",JSON.toJSONString(accessToken)); return accessTokenService.logout(accessToken); } }
这里主要做两个事情,一个是登录接口,一个是退出接口,逻辑我们使用AccessTokenService来进行处理。这里的AccessTokenService是我们自定义的,不是框架自带的。AccessTokenService完整代码如下:
package org.shop.oauth.server.service.impl; import java.security.Principal; import java.util.Map; import org.shop.common.model.http.BaseResponse; import org.shop.oauth.server.service.AccessTokenService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.oauth2.common.OAuth2AccessToken; import org.springframework.security.oauth2.common.OAuth2RefreshToken; import org.springframework.security.oauth2.provider.endpoint.TokenEndpoint; import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore; import org.springframework.stereotype.Service; import lombok.extern.slf4j.Slf4j; @Service @Slf4j public class AccessTokenServiceImpl implements AccessTokenService { @Autowired private TokenEndpoint tokenEndpoint; @Autowired private RedisTokenStore tokenStore; @Override public BaseResponse createNewToken(Principal principal, Map<String, String> parameters) throws Exception { //重新登录一遍 OAuth2AccessToken accessToken = tokenEndpoint.postAccessToken(principal, parameters).getBody(); //获取登录后的token String token = accessToken.getValue(); //把这个token从redis中给删除掉即可。 this.logoutAccessToken(token); //再重新登录生成新的token OAuth2AccessToken newAccessToken = tokenEndpoint.postAccessToken(principal, parameters).getBody(); return BaseResponse.ok(newAccessToken); } /** * 这里主要是相当于注销登录的功能,把token从redis中给删除掉 * @param token */ private void logoutAccessToken(String token) throws Exception{ log.info("准备注销"); //读取相关token的信息 OAuth2AccessToken accessToken = tokenStore.readAccessToken(token); if(null != accessToken) { //注销掉用户的token tokenStore.removeAccessToken(accessToken); //获取用户的refreshtoken OAuth2RefreshToken refreshToken = accessToken.getRefreshToken(); //注销掉refreshtoken tokenStore.removeRefreshToken(refreshToken); log.info("注销成功"); } } @Override public BaseResponse logout(String accessToken) throws Exception { this.logoutAccessToken(accessToken); return BaseResponse.ok(); } }
详细的流程我们已经在代码的注释里面写了,因此这里的话,我们就不再多做解释,然后我们运行下看看效果:
可以看到每次登录之后,这里的access_token和refresh_token都会变,并且同一个用户在多端登录也不会有影响:
最后按照惯例,附上本案例的源码,登录后即可下载。
还没有评论,来说两句吧...