前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >配合OAuth2进行单设备登录拦截 顶

配合OAuth2进行单设备登录拦截 顶

作者头像
算法之名
发布2019-08-20 09:47:00
1.2K0
发布2019-08-20 09:47:00
举报
文章被收录于专栏:算法之名算法之名

要进行单设备登录,在其他地点登录后,本地的其他操作会被拦截返回登录界面。

原理就在于要在登录时在redis中存储Session,进行操作时要进行Session的比对。

具体实现,假设我们的OAuth 2的登录调用接口如下:

共享Session,User模块跟OAuth模块都要设置

代码语言:javascript
复制
@Configuration
@EnableRedisHttpSession
public class SessionConfig {

}

Feign

代码语言:javascript
复制
@Component
@FeignClient("oauth-center")
public interface Oauth2Client {

    /**
     * 获取access_token<br>
     * 这是spring-security-oauth2底层的接口,类TokenEndpoint<br>
     *
     * @param parameters
     * @return
     * @see org.springframework.security.oauth2.provider.endpoint.TokenEndpoint
     */
    @PostMapping(path = "/api-o/oauth/token")
    Map<String, Object> postAccessToken(@RequestParam Map<String, String> parameters);

    /**
     * 删除access_token和refresh_token<br>
     * 认证中心的OAuth2Controller方法removeToken
     *
     * @param access_token
     */
    @DeleteMapping(path = "/api-o/remove_token")
    void removeToken(@RequestParam("access_token") String access_token);

}

Controller

代码语言:javascript
复制
/**
 * Created by Administrator on 2018/10/19.
 */
@Slf4j
@RestController
public class UserTokenController {
    @Autowired
    private Oauth2Client oauth2Client;
 
    @Resource
    private RedisService redisServiceImpl;

    /**
     * 系统登陆<br>
     * 根据用户名登录<br>
     * 采用oauth2密码模式获取access_token和refresh_token
     *
     * @param loginParam
     * @return
     */
    @PostMapping("/users-anon/sys/logins")
    public Map<String, Object> login(@RequestBody LoginParam loginParam,HttpServletRequest request) {

        Map<String, String> parameters = new HashMap<>();
        parameters.put(OAuth2Utils.GRANT_TYPE, "password");
        parameters.put(OAuth2Utils.CLIENT_ID, "system");
//        parameters.put(OAuth2Utils.CLIENT_ID, "system");
        parameters.put("client_secret", "system");
        parameters.put(OAuth2Utils.SCOPE, "app");
//    parameters.put("username", username);
        // 为了支持多类型登录,这里在username后拼装上登录类型
        parameters.put("username", loginParam.getUsername() + "|" + CredentialType.USERNAME.name());
        parameters.put("password", loginParam.getPassword());
        parameters.put("status","200");
        Map<String, Object> tokenInfo = null;
        try {
           tokenInfo = oauth2Client.postAccessToken(parameters);
        }catch (Exception e){
            e.printStackTrace();
            return ResponseUtils.getResult(500,"login failed");
        }
//        saveLoginLog(username, "用户名密码登陆", BlackIPAccessFilter.getIpAddress(request));

        return ResponseUtils.getDataResult(tokenInfo);
    }
}

加入Session的存储

代码语言:javascript
复制
/**
 * Created by Administrator on 2018/10/19.
 */
@Slf4j
@RestController
public class UserTokenController {
    @Autowired
    private Oauth2Client oauth2Client;

    @Resource
    private RedisService redisServiceImpl;

    /**
     * 系统登陆<br>
     * 根据用户名登录<br>
     * 采用oauth2密码模式获取access_token和refresh_token
     *
     * @param loginParam
     * @return
     */
代码语言:javascript
复制
@PostMapping("/users-anon/sys/logins")
    public Map<String, Object> login(@RequestBody LoginParam loginParam, HttpServletRequest request) {
        Map<String, String> parameters = new HashMap<>();
        parameters.put(OAuth2Utils.GRANT_TYPE, "password");
        parameters.put(OAuth2Utils.CLIENT_ID, "system");
//        parameters.put(OAuth2Utils.CLIENT_ID, "system");
        parameters.put("client_secret", "system");
        parameters.put(OAuth2Utils.SCOPE, "app");
//    parameters.put("username", username);
        // 为了支持多类型登录,这里在username后拼装上登录类型
        parameters.put("username", loginParam.getUsername() + "|" + CredentialType.USERNAME.name());
        parameters.put("password", loginParam.getPassword());
        parameters.put("status","200");
        Map<String, Object> tokenInfo = null;
        try {
           tokenInfo = oauth2Client.postAccessToken(parameters);
            HttpSession session = request.getSession();
            String sessionId = UUID.randomUUID().toString();
            //此处修改为共享Session
            session.setAttribute("sessionId", sessionId);
            session.setAttribute("username",loginParam.getUsername());
            String key = loginParam.getUsername() + "-onlyLogin";
            redisServiceImpl.set(key,sessionId);
            redisServiceImpl.expire(key,30 * 60);
            redisServiceImpl.hset("sessionHash",sessionId,loginParam.getUsername());
        }catch (Exception e){
            e.printStackTrace();
            return ResponseUtils.getResult(500,"login failed");
        }
//        saveLoginLog(username, "用户名密码登陆", BlackIPAccessFilter.getIpAddress(request));
        return ResponseUtils.getDataResult(tokenInfo);
    }
代码语言:javascript
复制
}

配置拦截器

代码语言:javascript
复制
@Slf4j
@Component
public class RedisInterceptor extends HandlerInterceptorAdapter {
    @Resource
    private RedisService redisServiceImpl;

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {

    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HttpSession session = request.getSession();
        //读取共享Session
        String requestedSessionId = (String) session.getAttribute("sessionId");
        String userName = null;
        response.setCharacterEncoding("utf-8");
        response.setContentType("text/javascript;charset=utf-8");
        try {
            if (!StringUtils.isEmpty(requestedSessionId)) {
                userName = redisServiceImpl.hget("sessionHash", requestedSessionId);
            }
            if (StringUtils.isEmpty(userName)) {
                response.getWriter().write("{\"message\":\"请先登陆\"}");
                return false;
            } else {
                String cacheSessionId = null;
                String sessionKey = userName + "-onlyLogin";
                try {
                    cacheSessionId = redisServiceImpl.get(sessionKey);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                if (StringUtils.isEmpty(cacheSessionId)) {
                    response.getWriter().write("{\"message\":\"请先登陆\"}");
                    return false;
                } else {
                    if (!cacheSessionId.equals(requestedSessionId)) {
                        response.getWriter().write("{\"message\":\"您的账号已在别处登陆,请重新登陆\"}");
                        return false;
                    } else {
                        redisServiceImpl.expire(sessionKey, 30 * 60);
                        return super.preHandle(request, response, handler);
                    }
                }
            }
        }catch (Exception e) {
            e.printStackTrace();
        }
        response.getWriter().write("{\"message\":\"服务器忙\"}");
        return false;
    }
}

拦截器就是为了获取每次的Session,并且跟redis中的session进行比对,如果session不同,则进行拦截。

代码语言:javascript
复制
@Configuration
public class RedisSessionConfig extends WebMvcConfigurerAdapter {
    @Autowired
    private RedisInterceptor redisInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(redisInterceptor).excludePathPatterns("/**/users-anon/**").excludePathPatterns("/api-o/oauth/token");
        super.addInterceptors(registry);
    }
}

这里要配置对登录的url以及feign的url进行放行,则可以对多地点登录时,使之前的登录无法操作。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云数据库 Redis
腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档