spring+springmvc+Interceptor+jwt+redis实现sso单点登录

一、前言

1.在大型互联网公司下,一个公司有多个平台系统项目,而且有可能每个项目开发语言都不同,同时每个平台也会分别有APP、PC(前后端分离)、小程序端等,而且服务部署在分布式环境下,要实现支持PC、APP(ios、android)、小程序等多端的会话共享,本文章采取的方案是spring+springmvc+Interceptor+jwt+redis实现sso单点登录。假如用传统方式session、cookie实现不太好,第一如果用session实现,会面临集群环境下session共享问题(可以用springsession来解决),但是session存过多数据会给服务器带来压力。第二如果用cookie实现,会面临域名跨域问题,而且不适合用在native app里面:native app不好管理cookie,毕竟它不是浏览器。
2.通俗来讲,JWT是一个含签名并携带用户相关信息的加密串,页面请求校验登录接口时,请求头中携带JWT串到后端服务,后端通过签名加密串匹配校验,保证信息未被篡改。校验通过则认为是可靠的请求,将正常返回数据,同时JSON解析成对象。
优点:在非跨域环境下使用JWT机制是一个非常不错的选择,实现方式简单,操作方便,能够快速实现。由于服务端不存储用户状态信息,因此大用户量,对后台服务也不会造成压力;
缺点:跨域实现相对比较麻烦,安全性也有待探讨。因为JWT令牌返回到页面中,可以使用js获取到,如果遇到XSS攻击令牌可能会被盗取,在JWT还没超时的情况下,就会被获取到敏感数据信息(敏感信息最好别放置JWT中)。
但是我们可以针对令牌盗取,做一些防范:比如前后端传输参数RSA验签防止篡改,采取https加密参数传输。没有一个互联网平台做到绝对安全的,只能说提高安全性,把风险降到最低。

二、实现代码

2.1.引入jwt的相关jar包,在项目pom.xml中引入:

		
		    com.auth0
		    java-jwt
		    2.2.0
		 

2.2 JWT工具类

public class JWT {
	private static final String SECRET = "填写你的秘钥";

    private static final String EXP = "exp";

    private static final String PAYLOAD = "payload";

    //加密,传入一个对象和有效期
    public static  String sign(T object, long maxAge) {
        try {
            final JWTSigner signer = new JWTSigner(SECRET);
            final Map claims = new HashMap();
            ObjectMapper mapper = new ObjectMapper();
            String jsonString = mapper.writeValueAsString(object);
            claims.put(PAYLOAD, jsonString);
            claims.put(EXP, System.currentTimeMillis() + maxAge);
            return signer.sign(claims);
        } catch(Exception e) {
            return null;
        }
    }

    //解密,传入一个加密后的token字符串和解密后的类型
    public static T unsign(String jwt, Class classT) {
        final JWTVerifier verifier = new JWTVerifier(SECRET);
        try {
            final Map claims= verifier.verify(jwt);
            if (claims.containsKey(EXP) && claims.containsKey(PAYLOAD)) {
                //long exp = (Long)claims.get(EXP);
                //long currentTimeMillis = System.currentTimeMillis();
                //if (exp > currentTimeMillis) {
                    String json = (String)claims.get(PAYLOAD);
                    ObjectMapper objectMapper = new ObjectMapper();
                    return objectMapper.readValue(json, classT);
                //}
            }
            return null;
        } catch (Exception e) {
            return null;
        }
    }
    
    public static void main(String[] args) {
        System.out.println(System.currentTimeMillis());
    	//加密
    	String ss = sign("123444444444444444444444444444444444444444444444444444444444444444444;4a", 0);
		System.out.println(ss);
		//解密
		System.out.println(unsign("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1NTg5NDY4NDUzODksInBheWxvYWQiOiJcIjExMTFcIiJ9.jJwpdHgpNLjU1vL0DZhxtAsNEBG0ysXrJEvMf_X5OXk", String.class));
	}
}

2.3编写用户登录LoginInterceptor.java拦截器

public class LoginInterceptor implements HandlerInterceptor{
	
	public static final String REDIS_TOKEN_FLAG = "redis_token";
	
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		//过滤不匹配的请求
		PrintWriter writer = null;
		if(handler instanceof DefaultServletHttpRequestHandler){
			writer = response.getWriter();
			Map resultMap = ResponseCode.buildReturnMap(ResponseCode.REQUEST_URL_NOT_SERVICE, false);
        	responseMessage(response, writer, resultMap);
        	return false;
		}
		
		response.setCharacterEncoding("utf-8");
        String token = request.getHeader("token");
        //拼接redis的key=平台标识+登录方式+(会员表id或用户授权表id)
        //token不存在
        if(StringUtils.isEmpty(token)) {
        	writer = response.getWriter();
        	Map resultMap = LoginResponseCode.buildReturnMap(LoginResponseCode.LOGIN_TOKEN_NOT_NULL, false);
        	responseMessage(response, writer, resultMap);
        	return false;
        }

		//tokenRedisKey是redis存token的key
		String tokenRedisKey = LoginInterceptor.REDIS_TOKEN_FLAG + JWT.unsign(token, String.class);
		if(StringUtils.isEmpty(tokenRedisKey)){
			writer = response.getWriter();
			Map resultMap = LoginResponseCode.buildReturnMap(LoginResponseCode.USERID_NOT_UNAUTHORIZED, false);
			responseMessage(response, writer, resultMap);
			return false;
		}

        //根据token截取用户id
        String redisToken = (String)RedisClusterUtils.get(tokenRedisKey);
        LoginResponseCode loginCode = null;
        if(StringUtils.isEmpty(redisToken)){
        	//未登录
        	loginCode = LoginResponseCode.LOGIN_TIME_EXP;
        }
        else if(!StringUtils.equals(redisToken, token)){
			//用户在同设备(APP、PC、小程序、公众号,比如APP设备不能登录两个一样的账号)上登录,你已经被踢下线
			loginCode= LoginResponseCode.RESPONSE_CODE_LOGIN_ERROR;
        }
        if(loginCode != null){
        	writer = response.getWriter();
        	Map resultMap = LoginResponseCode.buildReturnMap(loginCode, false);
        	responseMessage(response, writer, resultMap);
        	return false;
        }
        //重新设置会话时间,半小时
		RedisClusterUtils.expire(LoginInterceptor.REDIS_TOKEN_FLAG + token, 1800);
        request.setAttribute("tokenRedisKey", tokenRedisKey);
		return true;
	}

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

	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
			throws Exception {
		
	}
	
	private void responseMessage(HttpServletResponse response, PrintWriter out, Object responseVO) {
        response.setContentType("application/json; charset=utf-8");  
        out.print(JSONObject.fromObject(responseVO).toString());
        out.flush();
        out.close();
    }

}

2.4 定义LoginResponseCode

public enum LoginResponseCode {
	
	USERID_NOT_NULL(3001,"用户id不能为空."), 
	LOGIN_TOKEN_NOT_NULL(3002,"登录token不能为空."),
	USERID_NOT_UNAUTHORIZED(3003, "用户token无效"),
	RESPONSE_CODE_UNLOGIN_ERROR(421, "未登录异常"),
	RESPONSE_CODE_LOGIN_ERROR(422, "用户在另一台机器上登录,你已经被踢下线"),
	LOGIN_TIME_EXP(3004, "未登录或登录时间超时,请重新登录");
	
    // 成员变量  
	private int code; //状态码  
    private String message; //返回消息

    // 构造方法  
    private LoginResponseCode(int code,String message) {  
        this.code = code;  
        this.message = message;  
    }  
	public int getCode() {
		return code;
	}
	public void setCode(int code) {
		this.code = code;
	}
	public String getMessage() {
		return message;
	}
	public void setMessage(String message) {
		this.message = message;
	}  

	public static ResponseVO buildEnumResponseVO(LoginResponseCode responseCode, Object data) {
		return new ResponseVO(responseCode.getCode(),responseCode.getMessage(),data);
	}
	
	public static Map buildReturnMap(LoginResponseCode responseCode, Object data) {
		Map map = new HashMap();
		map.put("code", responseCode.getCode());
		map.put("message", responseCode.getMessage());
		map.put("data", data);
		return map;
	}
}

2.5 定义ResponseCode 枚举类

public enum ResponseCode {
	RESPONSE_CODE_FAILURE(10000,"请求失败、结果处理失败"),
	RESPONSE_CODE_SUCCESS(200,"请求成功、结果处理成功"),
	RESPONSE_CODE_PARAM_FORMAT_ERROR(10002,"请求失败、参数格式错误"),
	RESPONSE_CODE_PARAM_ERROR(10003,"参数错误"),
	RESPONSE_CODE_REQ_CANNOT_EMPTY(10004,"必要的请求参数不能为空"),
	RESPONSE_CODE_USER_DOES_NOT_EXIST(10005,"用户不存在"),
	RESPONSE_CODE_QUERY_SUCCESS(10006,"数据查询成功"),
	RESPONSE_CODE_QUERY_NO_DATA(10007,"无数据或者结果"),
	USER_LOGIN_PWD_ERROR(10008,"用户名密码错误"),
	REQUEST_URL_NOT_SERVICE(10009,"访问了非服务路径"),
	RESPONSE_CODE_QUERY_REMARKS_50(10010,"退货原因不得超过50字"),
	RESPONSE_CODE_UNLOGIN_ERROR(421,"未登录异常"),
	RESPONSE_CODE_LOGIN_SUCCESS(200,"用户登录成功"),
	RESPONSE_CODE_NO_PERMISSION(403,"无权访问该系统"),
	RESPONSE_CODE_SYSTEM_ERROR(500,"系统内部异常"),
	RESPONSE_CODE_TIME_OUT(504,"上游服务端网关超时"),
	REGISTER_MOBILE_EXIST(2001,"手机号码已经注册"),
	REGISTER_VERIFICA_CODE_INVALID(2002,"验证码错误或者已经失效,请重新获取验证码"),
	VERIFICA_CODE_ERROR(2003,"验证码不正确或失效"),
	REFERRER_NOT_EXIST(2004, "推荐人不存在"),
	PROXY_ACTIVATE_CODE_ERROR(2005, "激活码错误"),
	USER_TRADE_PASSWORD_ERROR(2006,"用户交易密码错误"),
	APP_VERSION_MISMATCHING(2007,"app版本号不是最新版本"),
	USER_IDNO_NOT_MATCHING(2008, "用户身份证号码不匹配"),
	BANK_CARD_MUST_HAVE_TO_ONE(2010, "银行卡必须保留一张"),
	OLD_PASSWORD_ERROR(2011, "原密码不正确"),
	NEW_PASSWORD_INCONSISTENT(2012, "新密码不一致"),
	V_PASSWORD_REG(2013, "密码为数字或字母"),
	TWO_PASSWORD_INCONSISTENCIES(2014, "密码和确认密码不一致"),
	ACCEPT_PROTOCOL(2015, "请阅读和接受协议"),
	LOGOUT_SUCESS(2016, "退出成功"),
	TRANSCODE_FAILED(2017,"转码失败"),
	APPLY_SALE_AFTER_STATUS_ERROR(2018,"申请售后失败,该订单未付款"),
	NO_PAY_VALIDATE(2019,"支付未认证"),
	PAY_VALIDATE_PROCESSING(2020,"支付人工审核中"),
	PAY_VALIDATE_FAIL(2021,"支付人工审核失败,请重新填写"),
	PAY_VALIDATE_SUCESS(2022,"支付人工审核成功"),
	ACCOUNT_EXIST(2023,"账号已存在"),
	ROLE_NAME_EXIST(2024,"角色已存在"),
	PASSWORD_IS_BLENK(2025,"密码不能为空"),
	DECRYPT_FAIL(2026,"参数解密失败"),
	NOT_BINDING_MEMBER(2027,"未绑定会员"),
	GET_ACCESSTOKEN_FAIL(2028,"获取accessToken失败"),
	TWO_PASSWORD_CONSISTENCIES(2029, "新旧密码一致"),
    NOT_PLATFORM_AUTH(2030, "该平台账号未授权或者已停用"),
	AUTH_CODE_NOT_NULL(2031, "平台授权码不能为空");

	private int code; //状态码
	private String message; //返回消息
	private static String version = "v1.0"; //版本号

	// 构造方法
	private ResponseCode(int code,String message) {
		this.code = code;
		this.message = message;
	}
	public int getCode() {
		return code;
	}
	public void setCode(int code) {
		this.code = code;
	}
	public String getMessage() {
		return message;
	}
	public void setMessage(String message) {
		this.message = message;
	}

	public static ResponseVO buildEnumResponseVO(ResponseCode responseCode, Object data) {
		return new ResponseVO(responseCode.getCode(),responseCode.getMessage(),data);
	}

	public static Map buildReturnMap(ResponseCode responseCode, Object data) {
		Map map = new HashMap();
		map.put("code", responseCode.getCode());
		map.put("version", version);
		map.put("message", responseCode.getMessage());
		map.put("data", data);
		return map;
	}


	/**
	 * 自定义状态码返回结果Map
	 * @param code  状态码
	 * @param message 消息提示
	 * @param data 数据
	 * @return
	 */
	public static Map customBuildReturnMap(String code, String message, Object data) {
		if(StringUtils.isEmpty(code)){
			code = "9999"; // 自定义错误消息状态码
		}
		Map map = new HashMap();
		map.put("code", code);
		map.put("version", version);
		map.put("message", message);
		map.put("data", data);
		return map;
	}
}

2.6 RedisClusterUtils(redis集群工具类)

public class RedisClusterUtils {

	@SuppressWarnings("rawtypes")
	private static RedisTemplate redisTemplate;

	public void setRedisTemplate(RedisTemplate redisTemplate) {
		RedisClusterUtils.redisTemplate = redisTemplate;
	}

	static {
		if (null == redisTemplate) {
			RedisClusterUtils.redisTemplate = SpringContextHolder
					.getBean(RedisTemplate.class);
		}
	}

	/**
	 * 普通缓存获取
	 * 
	 * @param key
	 *            键
	 * @return 值
	 */
	public static Object get(String key) {
		return key == null ? null : redisTemplate.opsForValue().get(key);
	}

	/**
	 * 普通缓存放入
	 * 
	 * @param key
	 *            键
	 * @param value
	 *            值
	 * @return true成功 false失败
	 */
	public static boolean set(String key, Object value) {
		try {
			redisTemplate.opsForValue().set(key, value);
			return true;
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}

	}

	/**
	 * 普通缓存放入并设置时间
	 * 
	 * @param key
	 *            键
	 * @param value
	 *            值
	 * @param time
	 *            时间(秒) time要大于0 如果time小于等于0 将设置无限期
	 * @return true成功 false 失败
	 */
	public static boolean set(String key, Object value, long time) {
		try {
			if (time > 0) {
				redisTemplate.opsForValue().set(key, value, time,
						TimeUnit.SECONDS);
			} else {
				set(key, value);
			}
			return true;
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
	}

	/**
	 * 递增
	 * 
	 * @param key
	 *            键
	 * @param by
	 *            要增加几(大于0)
	 * @return
	 */
	public static long incr(String key, long delta) {
		if (delta < 0) {
			throw new RuntimeException("递增因子必须大于0");
		}
		return redisTemplate.opsForValue().increment(key, delta);
	}

	/**
	 * 递减
	 * 
	 * @param key
	 *            键
	 * @param by
	 *            要减少几(小于0)
	 * @return
	 */
	public static long decr(String key, long delta) {
		if (delta < 0) {
			throw new RuntimeException("递减因子必须大于0");
		}
		return redisTemplate.opsForValue().increment(key, -delta);
	}

	/**
	 * HashGet
	 * 
	 * @param key
	 *            键 不能为null
	 * @param item
	 *            项 不能为null
	 * @return 值
	 */
	public static Object hget(String key, String item) {
		return redisTemplate.opsForHash().get(key, item);
	}

	public static Object hget(byte[] key, byte[] item) {
		RedisSerializer stringSerializer = new StringRedisSerializer();
		redisTemplate.setHashValueSerializer(stringSerializer);
		return redisTemplate.opsForHash().get(key, item);
	}

	/**
	 * 获取hashKey对应的所有键值
	 * 
	 * @param key
	 *            键
	 * @return 对应的多个键值
	 */
	public static Map hmget(String key) {
		RedisSerializer stringSerializer = new StringRedisSerializer();
		redisTemplate.setHashValueSerializer(stringSerializer);
		return redisTemplate.opsForHash().entries(key);
	}

	/**
	 * 
	 * @Title: hkeys
	 * @Description: 获取hash所有字段
	 * @param @param key
	 * @param @return 设定文件
	 * @return Set 返回类型
	 * @throws
	 */
	public static Set hkeys(String key) {
		return redisTemplate.opsForHash().keys(key);
	}

	/**
	 * HashSet
	 * 
	 * @param key
	 *            键
	 * @param map
	 *            对应多个键值
	 * @return true 成功 false 失败
	 */
	public static boolean hmset(String key, Map map) {
		try {
			redisTemplate.opsForHash().putAll(key, map);
			return true;
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
	}

	/**
	 * HashSet 并设置时间
	 * 
	 * @param key
	 *            键
	 * @param map
	 *            对应多个键值
	 * @param time
	 *            时间(秒)
	 * @return true成功 false失败
	 */
	public static boolean hmset(String key, Map map, long time) {
		try {
			redisTemplate.opsForHash().putAll(key, map);
			if (time > 0) {
				expire(key, time);
			}
			return true;
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
	}

	/**
	 * 向一张hash表中放入数据,如果不存在将创建
	 * 
	 * @param key
	 *            键
	 * @param item
	 *            项
	 * @param value
	 *            值
	 * @return true 成功 false失败
	 */
	public static boolean hset(String key, String item, Object value) {
		try {
			redisTemplate.opsForHash().put(key, item, value);
			return true;
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
	}

	public static boolean hset(byte[] key, byte[] item, byte[] value) {
		try {
			redisTemplate.opsForHash().put(key, item, value);
			return true;
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
	}

	/**
	 * 向一张hash表中放入数据,如果不存在将创建
	 * 
	 * @param key
	 *            键
	 * @param item
	 *            项
	 * @param value
	 *            值
	 * @param time
	 *            时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
	 * @return true 成功 false失败
	 */
	public static boolean hset(String key, String item, Object value, long time) {
		try {
			redisTemplate.opsForHash().put(key, item, value);
			if (time > 0) {
				expire(key, time);
			}
			return true;
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
	}

	/**
	 * 删除hash表中的值
	 * 
	 * @param key
	 *            键 不能为null
	 * @param item
	 *            项 可以使多个 不能为null
	 */
	public static void hdel(String key, Object... item) {
		redisTemplate.opsForHash().delete(key, item);
	}

	public static void hdel(byte[] key, Object... item) {
		redisTemplate.opsForHash().delete(key, item);
	}

	/**
	 * 判断hash表中是否有该项的值
	 * 
	 * @param key
	 *            键 不能为null
	 * @param item
	 *            项 不能为null
	 * @return true 存在 false不存在
	 */
	public static boolean hHasKey(String key, String item) {
		return redisTemplate.opsForHash().hasKey(key, item);
	}

	/**
	 * hash递增 如果不存在,就会创建一个 并把新增后的值返回
	 * 
	 * @param key
	 *            键
	 * @param item
	 *            项
	 * @param by
	 *            要增加几(大于0)
	 * @return
	 */
	public static double hincr(String key, String item, double by) {
		return redisTemplate.opsForHash().increment(key, item, by);
	}

	/**
	 * hash递减
	 * 
	 * @param key
	 *            键
	 * @param item
	 *            项
	 * @param by
	 *            要减少记(小于0)
	 * @return
	 */
	public static double hdecr(String key, String item, double by) {
		return redisTemplate.opsForHash().increment(key, item, -by);
	}

	/**
	 * 根据key获取Set中的所有值
	 * 
	 * @param key
	 *            键
	 * @return
	 */
	public static Set sGet(String key) {
		try {
			return redisTemplate.opsForSet().members(key);
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	/**
	 * 根据value从一个set中查询,是否存在
	 * 
	 * @param key
	 *            键
	 * @param value
	 *            值
	 * @return true 存在 false不存在
	 */
	public static boolean sHasKey(String key, Object value) {
		try {
			return redisTemplate.opsForSet().isMember(key, value);
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
	}

	/**
	 * 将数据放入set缓存
	 * 
	 * @param key
	 *            键
	 * @param values
	 *            值 可以是多个
	 * @return 成功个数
	 */
	public static long sSet(String key, Object... values) {
		try {
			return redisTemplate.opsForSet().add(key, values);
		} catch (Exception e) {
			e.printStackTrace();
			return 0;
		}
	}

	/**
	 * 将set数据放入缓存
	 * 
	 * @param key
	 *            键
	 * @param time
	 *            时间(秒)
	 * @param values
	 *            值 可以是多个
	 * @return 成功个数
	 */
	public static long sSetAndTime(String key, long time, Object... values) {
		try {
			Long count = redisTemplate.opsForSet().add(key, values);
			if (time > 0)
				expire(key, time);
			return count;
		} catch (Exception e) {
			e.printStackTrace();
			return 0;
		}
	}

	/**
	 * 获取set缓存的长度
	 * 
	 * @param key
	 *            键
	 * @return
	 */
	public static long sGetSetSize(String key) {
		try {
			return redisTemplate.opsForSet().size(key);
		} catch (Exception e) {
			e.printStackTrace();
			return 0;
		}
	}

	/**
	 * 移除值为value的
	 * 
	 * @param key
	 *            键
	 * @param values
	 *            值 可以是多个
	 * @return 移除的个数
	 */
	public static long setRemove(String key, Object... values) {
		try {
			Long count = redisTemplate.opsForSet().remove(key, values);
			return count;
		} catch (Exception e) {
			e.printStackTrace();
			return 0;
		}
	}

	/**
	 * 获取list缓存的内容
	 * 
	 * @param key
	 *            键
	 * @param start
	 *            开始
	 * @param end
	 *            结束 0 到 -1代表所有值
	 * @return
	 */
	public static List lGet(String key, long start, long end) {
		try {
			return redisTemplate.opsForList().range(key, start, end);
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	/**
	 * 获取list缓存的长度
	 * 
	 * @param key
	 *            键
	 * @return
	 */
	public static long lGetListSize(String key) {
		try {
			return redisTemplate.opsForList().size(key);
		} catch (Exception e) {
			e.printStackTrace();
			return 0;
		}
	}

	/**
	 * 通过索引 获取list中的值
	 * 
	 * @param key
	 *            键
	 * @param index
	 *            索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
	 * @return
	 */
	public static Object lGetIndex(String key, long index) {
		try {
			return redisTemplate.opsForList().index(key, index);
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	/**
	 * 将list放入缓存
	 * 
	 * @param key
	 *            键
	 * @param value
	 *            值
	 * @param time
	 *            时间(秒)
	 * @return
	 */
	public static boolean lSet(String key, Object value) {
		try {
			redisTemplate.opsForList().rightPush(key, value);
			return true;
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
	}

	/**
	 * 将list放入缓存
	 * 
	 * @param key
	 *            键
	 * @param value
	 *            值
	 * @param time
	 *            时间(秒)
	 * @return
	 */
	public static boolean lSet(String key, Object value, long time) {
		try {
			redisTemplate.opsForList().rightPush(key, value);
			if (time > 0)
				expire(key, time);
			return true;
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
	}

	/**
	 * 将list放入缓存
	 * 
	 * @param key
	 *            键
	 * @param value
	 *            值
	 * @param time
	 *            时间(秒)
	 * @return
	 */
	public static boolean lSet(String key, List value) {
		try {
			redisTemplate.opsForList().rightPushAll(key, value);
			return true;
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
	}

	/**
	 * 将list放入缓存
	 * 
	 * @param key
	 *            键
	 * @param value
	 *            值
	 * @param time
	 *            时间(秒)
	 * @return
	 */
	public static boolean lSet(String key, List value, long time) {
		try {
			redisTemplate.opsForList().rightPushAll(key, value);
			if (time > 0)
				expire(key, time);
			return true;
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
	}

	/**
	 * 根据索引修改list中的某条数据
	 * 
	 * @param key
	 *            键
	 * @param index
	 *            索引
	 * @param value
	 *            值
	 * @return
	 */
	public static boolean lUpdateIndex(String key, long index, Object value) {
		try {
			redisTemplate.opsForList().set(key, index, value);
			return true;
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
	}

	/**
	 * 移除N个值为value
	 * 
	 * @param key
	 *            键
	 * @param count
	 *            移除多少个
	 * @param value
	 *            值
	 * @return 移除的个数
	 */
	public static long lRemove(String key, long count, Object value) {
		try {
			Long remove = redisTemplate.opsForList().remove(key, count, value);
			return remove;
		} catch (Exception e) {
			e.printStackTrace();
			return 0;
		}
	}

	/**
	 * 获取byte[]类型Key
	 * 
	 * @param key
	 * @return
	 */
	public static byte[] getBytesKey(Object object) {
		if (object instanceof String) {
			return StringUtils.getBytes((String) object);
		} else {
			return ObjectUtils.serialize(object);
		}
	}

	/**
	 * Object转换byte[]类型
	 * 
	 * @param key
	 * @return
	 */
	public static byte[] toBytes(Object object) {
		return ObjectUtils.serialize(object);
	}

	/**
	 * byte[]型转换Object
	 * 
	 * @param key
	 * @return
	 */
	public static Object toObject(byte[] bytes) {
		return ObjectUtils.unserialize(bytes);
	}

	/**
	 * 指定缓存失效时间
	 * 
	 * @param key
	 *            键
	 * @param time
	 *            时间(秒)
	 * @return
	 */
	public static boolean expire(String key, long time) {
		try {
			if (time > 0) {
				redisTemplate.expire(key, time, TimeUnit.SECONDS);
			}
			return true;
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
	}

	/**
	 * 根据key 获取过期时间
	 * 
	 * @param key
	 *            键 不能为null
	 * @return 时间(秒) 返回0代表为永久有效
	 */
	public static long getExpire(String key) {
		return redisTemplate.getExpire(key, TimeUnit.SECONDS);
	}

	/**
	 * 判断key是否存在
	 * 
	 * @param key
	 *            键
	 * @return true 存在 false不存在
	 */
	public static boolean hasKey(String key) {
		try {
			return redisTemplate.hasKey(key);
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
	}

	/**
	 * 删除缓存
	 * 
	 * @param key
	 *            可以传一个值 或多个
	 */
	public static void del(String... key) {
		if (key != null && key.length > 0) {
			if (key.length == 1) {
				redisTemplate.delete(key[0]);
			} else {
				redisTemplate.delete(CollectionUtils.arrayToList(key));
			}
		}
	}

	/**
	 * 判断key是否存在
	 * 
	 * @param key
	 *            键
	 * @return true 存在 false不存在
	 */
	public static Collection values(String key) {
		return redisTemplate.opsForHash().values(key);
	}

}
 
  

2.7 redis集群配置spring-cluster-jedis.xml文件




	Jedis Configuration
	
	
	
		
	
		
		
		
		
	

	
	
		
		
			
				
					
					
				
				
					
					
				
				
					
					
				
				
					
					
				
				
					
					
				
				
					
					
				
			
		
	

	
	
		
		
		
	

	
	

	
	
		
		
		
            
        
		
	

2.8 redis集群配置属性文件

#--------------redis settings--------------
redis.keyPrefix=
redis.host1=${redis.host1}
redis.port1=${redis.port1}
redis.host2=${redis.host2}
redis.port2=${redis.port2}
redis.host3=${redis.host3}
redis.port3=${redis.port3}
redis.host4=${redis.host4}
redis.port4=${redis.port4}
redis.host5=${redis.host5}
redis.port5=${redis.port5}
redis.host6=${redis.host6}
redis.port6=${redis.port6}

redis.maxTotal=1000
redis.maxIdle=100
redis.maxWaitMillis=1000
redis.timeout=30000
redis.testOnBorrow=true
redis.password=

2.9 登录Controller

	@RequestMapping(value = "/login", method = RequestMethod.POST)
	public ResponseVO login(@RequestBody Member member, HttpServletRequest request, HttpServletResponse response) {
        //获取平台标识
        String strPlatformFlag = (String)request.getAttribute("platformFlag");
        System.out.println("==============strPlatformFlag:=============" + strPlatformFlag);
        member.setPlatformFlag(strPlatformFlag);

		//判断必要参数是否为空
		if (!StringUtils.isNotBlank(member.getLoginName()) || !StringUtils.isNotBlank(member.getPassword()) ||
                !StringUtils.isNotBlank(member.getPlatformFlag()) ||!StringUtils.isNotBlank(member.getLoginType()) ||
                !StringUtils.isNotBlank(member.getSource())) {
			return ResponseCode.buildEnumResponseVO(ResponseCode.RESPONSE_CODE_REQ_CANNOT_EMPTY, null);
		}

        //请求参数,登录日志记录
        String strParam = member.toString();
		
		//判断用户是否存在
		Member user = memberService.getByLoginName(member.getLoginName());
		if(user == null ){
			return ResponseCode.buildEnumResponseVO(ResponseCode.RESPONSE_CODE_USER_DOES_NOT_EXIST, null);
		}
        String strPassword = "";
        String password = "";
        try {
            //RSA解密密码
            strPassword = RSAUtils.decrypt(member.getPassword(), RSAUtils.getPrivateKey());
            member.setPassword(strPassword);

            password = MD5EncryptUtil.encryptMD5Code(member.getPassword());
        }catch (Exception e){
            return ResponseCode.buildEnumResponseVO(ResponseCode.DECRYPT_FAIL, null);
        }
        try {
            //判断密码是否正确并添加token
            if (StringUtils.equals(password, user.getPassword())) {
                String token = JWT.sign(member.getSource() + user.getId(), 0);
                //token在redis中的key等于redis_token+ 来源(0.公众号,1.PC,2.APP3.小程序) + 用户id
                //token在redis中的value等于key加密
                RedisClusterUtils.set(LoginInterceptor.REDIS_TOKEN_FLAG + member.getSource() + user.getId(), token, 1800);
                Map dataMap = new HashMap();
                dataMap.put("token", token);
                dataMap.put("mid", user.getId());
                return ResponseCode.buildEnumResponseVO(ResponseCode.RESPONSE_CODE_LOGIN_SUCCESS, JSONObject.fromObject(dataMap));
            }
        }catch (Exception e){
            return ResponseCode.buildEnumResponseVO(ResponseCode.RESPONSE_CODE_FAILURE, null);
        }
		return ResponseCode.buildEnumResponseVO(ResponseCode.USER_LOGIN_PWD_ERROR, false);
	}

你可能感兴趣的:(spring+springmvc+Interceptor+jwt+redis实现sso单点登录)