大家好,我是烤鸭:

今天给大家分享简单的单点登录的实现方式。

环境及jar包:

springboot     1.5.10

redis    2.9.0    (可以用tomcat的session,但是无法解决多个tomcat共享session的问题)

shiro    1.4.0

lombok    1.16.10    (可选,编译的时候自动生成get,set,toString()方法)

登录方法:

主要是用户完成登录,登录信息写在cookie,

服务器端存缓存(redis)中。

LoginService:

package xxx.xxx.system.service.impl;import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import com.alibaba.fastjson.JSONObject;
import xxx.xxx.common.constant.IMsgEnum;
import xxx.xxx.common.resp.BaseMsgResp;
import xxx.xxx.common.utils.MD5Utils;
import xxx.xxx.entity.sys.UserDO;
import xxx.xxx.redis.cache.RedisManager;
import xxx.xxx.system.service.LoginService;
import xxx.xxx.system.service.UserService;@Service
public class LoginServiceImpl implements LoginService {@Autowiredprivate UserService userService;@Overridepublic BaseMsgResp login(String username, String password) throws AuthenticationException{BaseMsgResp resp = new BaseMsgResp();password = MD5Utils.encrypt(username, password);UsernamePasswordToken token = new UsernamePasswordToken(username, password);Subject subject = SecurityUtils.getSubject();subject.login(token);//获取用户Map<String, Object> params = new HashMap<>();params.put("username", username);List<UserDO> list = userService.list(params);if(list.size() > 1) {resp = new BaseMsgResp(IMsgEnum.SYSTEM_ANOMALY.getMsgCode()+"",IMsgEnum.SYSTEM_ANOMALY.getMsgText());return resp;}//tokenSerializable id = subject.getSession().getId();//将token放入redisRedisManager manager = RedisManager.getRedisSingleton();manager.set("sys:login:user_token_"+id.toString(),list.get(0).getUserId()+"",60*30);//防止同一个账号同时登录manager.set("sys:user:id_"+list.get(0).getUserId(), id.toString(),60*30);//用户信息manager.set("sys:login:user_info_"+list.get(0).getUserId(), JSONObject.toJSONString(list.get(0)),60*30);return resp;}
}

RedisManager.java

用于redis初始化,和一些基本方法:

packagexxx.xxx.redis.cache;import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;/*** @author * @version V1.0*/import java.util.Set;
import java.util.concurrent.TimeUnit;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;import com.alibaba.fastjson.JSONObject;
import xxx.xxx.redis.config.RedisConstant;import lombok.Data;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;/****/
@Configuration
@ConfigurationProperties(value="jedis.pool")
@Data
public class RedisManager {private final Logger logger = LoggerFactory.getLogger(this.getClass());volatile static RedisManager redisSingleton;private String host;private int port;private int expire;private int timeout;private String password = "";private static JedisPool jedisPool = null;//第几个仓库private String database;public String getDatabase() {if(null == database || "".equals(database)) return "0";return database;}public void setDatabase(String database) {this.database = database;}public RedisManager() {}public static RedisManager getRedisSingleton() {if(redisSingleton == null) {synchronized (RedisManager.class) {if(redisSingleton == null) return new RedisManager();}}return redisSingleton;}/*** 初始化方法*/public void init() {if (jedisPool == null) {if (password != null && !"".equals(password)) {jedisPool = new JedisPool(new JedisPoolConfig(), host, port, timeout, password);} else if (timeout != 0) {jedisPool = new JedisPool(new JedisPoolConfig(), host, port, timeout);} else {jedisPool = new JedisPool(new JedisPoolConfig(), host, port);}}}/*** 给Redis中Set集合中某个key值设值* * @param key* @param value*/public void set(String key, String value) {Jedis jedis = null;try {jedis = jedisPool.getResource();jedis.select(Integer.parseInt(getDatabase()));jedis.set(key, value);} catch (Exception e) {logger.error("Jedis set 异常" + e.getMessage());} finally {if (jedis != null) {jedis.close();}}}/*** 从Redis中Set集合中获取key对应value值* * @param key*/public String get(String key) {Jedis jedis = null;try {jedis = jedisPool.getResource();jedis.select(Integer.parseInt(getDatabase()));return jedis.get(key);} catch (Exception e) {logger.error("Jedis get 异常" + e.getMessage());return null;} finally {if (jedis != null) {jedis.close();}}}/*** 给Redis中Set集合中某个key值设值* * @param key* @param value*/public void set(String key, String value, long time) {Jedis jedis = null;try {jedis = jedisPool.getResource();jedis.select(Integer.parseInt(getDatabase()));jedis.set(key, value);jedis.set(key, value, "XX", "EX", time);} catch (Exception e) {logger.error("Jedis set 异常" + e.getMessage());} finally {if (jedis != null) {jedis.close();}}}/*** * @Title: expire * @Description: 设置指定key的生存时间   * @return Long    成功 返回 1 * @throws*/public Long expire(String key,int seconds) {Jedis jedis = null;try {jedis = jedisPool.getResource();jedis.select(Integer.parseInt(getDatabase()));return jedis.expire(key, seconds);} catch (Exception e) {logger.error("Jedis expire 异常" + e.getMessage());return null;} finally {if (jedis != null) {jedis.close();}}}/*** * @Title: exist * @Description: 判断key是否存在   * @return Boolean    返回类型 * @throws*/public Boolean exist(String key) {Jedis jedis = null;try {jedis = jedisPool.getResource();jedis.select(Integer.parseInt(getDatabase()));return jedis.exists(key);} catch (Exception e) {logger.error("Jedis exist 异常" + e.getMessage());return null;} finally {if (jedis != null) {jedis.close();}}}}

LoginInterceptor:

每次请求都会经过拦截器,校验用户是否登录或者多个浏览器(客户端)登录。

如果用户已登录,用这次请求cookie中的token和缓存中的比较,如果不一致,就把之前缓存中的用户信息清空。

这样之前的用户如果再次操作,就会跳转到登陆页面。

package xxx.xxx.system.interceptor;import java.io.IOException;
import java.io.Serializable;
import java.lang.ProcessBuilder.Redirect;
import java.util.HashMap;
import java.util.Map;import javax.servlet.ServletOutputStream;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;import com.alibaba.fastjson.JSONObject;
import xxx.xxx.common.utils.LogUtils;
import xxx.xxx.redis.cache.RedisManager;
import xxx.xxx.system.utils.ShiroUtils;/*** ClassName: PlatformInterceptor date: 2015年12月30日 下午2:13:24 Description: 拦截器* * @author * @version* @since JDK 1.8*/
@Component
@EnableAutoConfiguration(exclude = ErrorMvcAutoConfiguration.class)
public class LoginInterceptor implements HandlerInterceptor {private static final Log logger = LogFactory.getLog(LoginInterceptor.class);@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {try {logger.info(LogUtils.getRequestLog(request));//校验用户是否已经登录,如果登录过,将之前用户踢掉,同时更新缓存中用户信息Subject subject = SecurityUtils.getSubject();Serializable token = subject.getSession().getId();RedisManager redisManager = RedisManager.getRedisSingleton();//获取用户idString userId = redisManager.get("sys:login:user_token_"+token.toString());if(StringUtils.isNotBlank(userId)) {String tokenPre = redisManager.get("sys:user:id_"+userId);if(!token.equals(tokenPre)) {//重定向到login.htmlredirect(request, response); return false;}else {Long expire = redisManager.ttl("sys:login:user_token_"+token.toString());//过期时间小于1分钟的,更新tokenif(expire < 1 * 60 * 1000) {redisManager.expire("sys:login:user_token_"+token.toString(), 60*30);}}}else {redirect(request, response); return false;}} catch (Exception e) {logger.info("preHandle="+e.getMessage());}return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,ModelAndView modelAndView) throws Exception {}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)throws Exception {}//对于请求是ajax请求重定向问题的处理方法public void redirect(HttpServletRequest request, HttpServletResponse response) throws IOException{//获取当前请求的路径String basePath = request.getScheme() + "://" + request.getServerName() + ":"  + request.getServerPort()+request.getContextPath();
//        response.getOutputStream().write("账号在别处登录。".getBytes("UTF-8"));//如果request.getHeader("X-Requested-With") 返回的是"XMLHttpRequest"说明就是ajax请求,需要特殊处理 否则直接重定向就可以了if("XMLHttpRequest".equals(request.getHeader("X-Requested-With"))){//告诉ajax我是重定向response.setHeader("REDIRECT", "REDIRECT");//告诉ajax我重定向的路径response.setHeader("CONTENTPATH", basePath+"/login");response.setStatus(HttpServletResponse.SC_FORBIDDEN);}else{response.sendRedirect(basePath + "/login");}}
}

ShiroUtils.java

shiro 工具类,获取subject对象。

package xxx.xxx.system.utils;import java.security.Principal;
import java.util.Collection;
import java.util.List;import org.apache.shiro.SecurityUtils;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;import xxx.xxx.entity.sys.UserDO;public class ShiroUtils {@Autowiredprivate static SessionDAO sessionDAO;public static Subject getSubjct() {return SecurityUtils.getSubject();}public static UserDO getUser() {Object object = getSubjct().getPrincipal();return (UserDO)object;}public static Integer getUserId() {return getUser().getUserId();}public static void logout() {getSubjct().logout();}public static List<Principal> getPrinciples() {List<Principal> principals = null;Collection<Session> sessions = sessionDAO.getActiveSessions();return principals;}
}

UserDO.java

用户对象。

package xxx.xxx.entity.sys;import org.springframework.format.annotation.DateTimeFormat;import java.io.Serializable;
import java.util.Date;
import java.util.List;public class UserDO implements Serializable {private static final long serialVersionUID = 1L;//用户idprivate Integer userId;// 用户名private String username;// 用户真实姓名private String name;// 密码private String password;
}

application.yml

jedis :pool :host : 127.0.0.1port : 9001password: abcd123maxTotal: 100maxIdle: 10maxWaitMillis : 100000

springboot redis shiro 实现 单点登录相关推荐

  1. SpringBoot+MyBatis+Redis实现SSO单点登录系统(二)

    SpringBoot+MyBatis+Redis实现SSO单点登录系统(二) 三.代码 配置文件配置数据库,redis等相关的信息. # See http://docs.spring.io/sprin ...

  2. SpringBoot+MyBatis+Redis实现SSO单点登录系统(一)

    SpringBoot+MyBatis+Redis实现SSO单点登录系统(一) 一.SSO系统概述 SSO英文全称Single Sign On,单点登录.SSO是在多个应用系统中,用户只需要登录一次就可 ...

  3. CAS5.3服务器搭建及SpringBoot整合CAS实现单点登录

    CAS5.3服务器搭建及SpringBoot整合CAS实现单点登录 1.1 什么是单点登录 1.2 什么是CAS 1.3 CAS服务端部署 1.template下载 1.4 客户端搭建 1.1 什么是 ...

  4. spring boot整合Shiro实现单点登录

    默认情况下,Shiro已经为我们实现了和Cas的集成,我们加入集成的一些配置就ok了 1.加入shiro-cas包 <!-- shiro整合cas单点 --><dependency& ...

  5. cas+shiro+spring 单点登录

    2019独角兽企业重金招聘Python工程师标准>>> 最近公司在搞单点登录,之前也做过,用的是58同城的wf框架,基于cas 的原理用拦截器自己写的一套.目前用cas+shiro+ ...

  6. SpringBoot+OAuth2+JWT实现单点登录SSO完整教程,竟如此简单优雅!

    作者:狂乱的贵公子 来源:https://www.cnblogs.com/cjsblog/p/10548022.html 1.前言 技术这东西吧,看别人写的好像很简单似的,到自己去写的时候就各种问题, ...

  7. 使用Redis实现CAS单点登录技术方案

    什么是单点登录 单点登录(Single Sign On),简称为 SSO,是目前比较流行的企业业务整合的解决方案之一.SSO的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统 ...

  8. springboot集成shiro实现用户登录认证

    Apache Shiro 是一个功能强大且易于使用的Java安全框架,可执行身份验证.授权.加密和会话管理.使用Shiro易于理解的API,您可以快速轻松地保护任何应用程序-从最小的移动应用程序到最大 ...

  9. SpringBoot + Redis + Shiro 实现权限管理

    文章主要是针对shiro进行权限配置,只针对角色进行了权限过滤. GitHub:https://github.com/stevencxb/blog 数据库脚本 -- ----------------- ...

最新文章

  1. 新手C#string类常用函数的学习2018.08.04
  2. nginx LB服务器配置
  3. 【pytorch】Metrics的工作原理
  4. 一个简单的更改让PyTorch读取表格数据的速度提高20倍:可大大加快深度学习训练的速度...
  5. R语言:na.fail和na.omit
  6. centos 限制只能访问某个目录的php文件
  7. 25. 合并两个排序的链表
  8. 设计师网站导航的好处
  9. 项目1:交换机后台管理程序(待续1……)
  10. python 集合、函数、文件操作
  11. Vue:vue组件生命周期
  12. 大学“电路分析基础”试题合集第八章
  13. python实现规则引擎_几种开源规则引擎(BRE)的比较 转
  14. 戴尔云客户端:三大发力点实现高速增长
  15. 这是我见过最好的唐诗,而且通俗易懂5
  16. 360手机:360N4S骁龙版 Twrp、Root、Magisk教程
  17. 激光功率的测量方案和设备
  18. 特斯拉Y系列降价后订单暴增,国信证券称后期有望降至26万
  19. linux svn 配置文件路径,linux下svn目录访问权限的配置
  20. 朴素贝叶斯分类算法--终极奥义

热门文章

  1. [html] 你知道短链接的生成原理吗?
  2. [css] 使用css3实现一个斑马线的效果
  3. [vue] vue中怎么重置data?
  4. 前端学习(2847):css鼠标样式
  5. 工作126:Vue.ls.set传值 然后取值
  6. “约见”面试官系列之常见面试题第三十八篇之js常见的继承方式(建议收藏)
  7. linux lnmp yum版安装
  8. php redis 删除元素,redisTemplate.delete()不能删除元素
  9. css 两行字变成一行,如何通过CSS将两行文本更改为一行?
  10. 计算机专业自然辩证法期末论文,清华大学自然辩证法-期末论文.docx