springboot redis shiro 实现 单点登录
生活随笔
收集整理的這篇文章主要介紹了
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對象。
UserDO.java
? ? 用戶對象。
application.yml
jedis :pool :host : 127.0.0.1port : 9001password: abcd123maxTotal: 100maxIdle: 10maxWaitMillis : 100000總結
以上是生活随笔為你收集整理的springboot redis shiro 实现 单点登录的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: html画特殊图形(待修改)
- 下一篇: Vue中用到jeDate日期控件,Vue