springboot redis shiro 实现 单点登录

大家好,我是烤鸭:

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

    环境及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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/412979.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

IntelliJ IDEA 控制台最大化

快捷键一&#xff1a; ctrlshift方向键上下 快捷键二&#xff1a; CtrlShift"

Centos7安装Hadoop教程

一&#xff1a;安装SSH 1&#xff0c;执行下面的命令安装ssh yum install openssh-clients yum install openssh-server 2&#xff0c;执行如下命令测试一下 SSH 是否可用&#xff08;SSH首次登陆提示 yes/no 信息&#xff0c;输入 yes 即可&#xff0c;然后按照提示输入 root…

[css] 请举例说明css有哪些不可继承的属性?

[css] 请举例说明css有哪些不可继承的属性&#xff1f; 1、display&#xff1a;规定元素应该生成的框的类型2、文本属性&#xff1a;vertical-align&#xff1a;垂直文本对齐text-decoration&#xff1a;规定添加到文本的装饰text-shadow&#xff1a;文本阴影效果white-space&…

elasticsearch 6.x (一) 部署 windows入门 spingboot连接

大家好&#xff0c;我是烤鸭&#xff1a;今天分享的是 elasticsearch 6.x 部署 windows服务器。环境&#xff1a;win10elasticsearch-6.2.4springboot 2.0.0.RELEASE1. 官网下载elasticsearch这个是最新版本的es下载地址。https://www.elastic.co/downloads/elasticsearch选择z…

Programming Assignment 5: Burrows–Wheeler Data Compression

Programming Assignment 5: Burrows–Wheeler Data Compression 1. 题目阅读 实现Burrows-Wheeler数据压缩算法。这个革命性的算法产生了gzip和pkzip&#xff0c;并且相对容易实现&#xff0c;还不受任何专利保护。它构成了unix压缩实用程序bzip2的基础。 这个算法由以下三种算…

hadoop Connection refused: no further information原因排查(Centos7)

一&#xff1a;排查防火墙&#xff0c;是否开放9000端口 firewall-cmd --list-ports查看防火墙是否有9000端口&#xff0c;如果没有&#xff0c;通过下面的命令添加端口&#xff1a; firewall-cmd --zonepublic --add-port9000/tcp --permanent systemctl restart firewalld…

[css]你有使用过preload、preconnect、prefetch这些属性吗?说说它们都有什么作用?

[css]你有使用过preload、preconnect、prefetch这些属性吗&#xff1f;说说它们都有什么作用&#xff1f; preload 元素的 rel 属性的属性值preload能够让你在你的HTML页面中元素内部书写一些声明式的资源获取请求&#xff0c;可以指明哪些资源是在页面加载完成后即刻需要的。…

elasticsearch 6.x (二) linux部署 kibana x-pack 安装

大家好&#xff0c;我是烤鸭&#xff1a; 环境&#xff1a;linux Cent OS 7.3elasticsearch-6.2.4 1. 下载elasticsearch https://www.elastic.co/downloads/elasticsearch 上面的网址直接下载的话&#xff0c;实在太慢了。官方还提供了另一种方式。 https://www.elastic.co…

Kali Linux ——在无网络情况下安装无线网卡驱动

1、背景&#xff1a; 今日刚刚开始学习kali linux&#xff0c;众所周知&#xff0c;安装完成后&#xff0c;系统是没有无线网卡驱动的&#xff0c;这就对学生党造成相当的困扰&#xff1a;校园网要连接有线是需要认证客户端的&#xff0c;而认证客户端只有windows端&#xff0c…

HADOOP_HOME and hadoop.home.dir are unset 报错处理

一般是windows才会出现这个问题 请看下面的解决方案&#xff1a; 第一步&#xff1a;下载winutils-master.zip Gitee地址&#xff1a;https://gitee.com/nkuhyx/winutils.git 蓝奏云&#xff1a;https://www.lanzoux.com/i55ccnc Github地址&#xff1a;https://github.com/cda…

[css] 你是怎样对css文件进行压缩合并的?

[css] 你是怎样对css文件进行压缩合并的&#xff1f; 使用在线网站进行压缩&#xff0c;如http://tool.lu/css如使用Gulp&#xff0c;可使用gulp-minify-css进行压缩如使用WebPack&#xff0c;可使用optimize-css-assets-webpack-plugin进行压缩个人简介 我是歌谣&#xff0c;…

elasticsearch 6.x (三) linux 集群多节点部署

大家好&#xff0c;我是烤鸭&#xff1a;关于集群内单个节点部署&#xff0c;请参考上一篇文章。elasticsearch 6.x linux部署(二) kibana x-pack 安装环境&#xff1a;linux Cent OS 7.3elasticsearch-6.2.41. 下载多个es安装每个安装步骤都是一样的。2. 修改配置文件(重…

ztree改变节点颜色

//找到节点对象 var node ztree.getNodesByParam("id",aaaaaaaaaabbbbbb, null)[0]; if(node!null){//找到span标签&#xff0c;并改变颜色$("#"node.tId"_span").css("color",red); }

版本下载地址

http://chromedriver.storage.googleapis.com/index.html转载于:https://www.cnblogs.com/nvhanzhi/p/9887999.html

[css] css3和css2的区别是什么?

[css] css3和css2的区别是什么&#xff1f; css3增加了更多特性&#xff1a;动画、过渡效果&#xff0c;圆角、文字特效等个人简介 我是歌谣&#xff0c;欢迎和大家一起交流前后端知识。放弃很容易&#xff0c; 但坚持一定很酷。欢迎大家一起讨论 主目录 与歌谣一起通关前端…

springboot-devtools idea或eclipse 热加载

大家好&#xff0c;我是烤鸭&#xff1a;今天分享一下springboot项目的热加载。第二种方式在eclipse和idea中都可以。虽然会有一些小坑。 方式有两种&#xff1a; 1. springloaded(无效) <!-- https://mvnrepository.com/artifact/org.springframework/springloaded -->…

PostgreSQL创建数据库报错

ERROR:source database "template1"is being accessed by other users DETAIL:There are 2 other sessions using the database. 解决方案&#xff1a; CREATE DATABASE 数据库名称 WITH OWNER postgres ENCODING UTF8 TABLESPACE pg_default LC_COLLATE en_US.…

[css] 你知道什么是流体排版吗?说说它的原理是什么?

[css] 你知道什么是流体排版吗&#xff1f;说说它的原理是什么&#xff1f; 在文档流中&#xff0c;内联元素按内联方向显示&#xff0c;即词语在依据文件写作模式的句子中表示的方向。块元素则一个接一个地显示&#xff0c;就像该文档的写作模式中的段落一样。因此在流体排版…

java统计报表日期工具类

package com.test.common;import com.coyee.core.util.DateUtil;import java.sql.Timestamp; import java.text.SimpleDateFormat; import java.util.*;/*** 时间工具类*/ public class DateUtils {/*<option value"today">今天</option><option valu…

springboot mybatis 热加载mapper.xml文件(最简单)

大家好&#xff0c;我是烤鸭: 今天介绍一下springboot mybatis 热加载mapper.xml文件。 本来不打算写的&#xff0c;看到网上比较流行的方式都比较麻烦&#xff0c;想着简化一下。 网上流行的版本。 https://www.cnblogs.com/oskyhg/p/8587701.html 总结一下需要&#xff1a;my…