1. 程式人生 > >分散式環境中的登入應用

分散式環境中的登入應用

在分散式環境中使用kapcha和redis完成登入功能。

1.新增jar包

<!-- kaptcha -->  
<dependency>  
    <groupId>com.google.code.kaptcha</groupId>  
    <artifactId>kaptcha</artifactId>  
    <version>2.3.2</version>  
</dependency>
2.在spring-mvc-servlet.xml中新增攔截器和captchaProducer bean
    <mvc:status-controller status-code="200" path="/status"/>
    <mvc:view-controller path="/" view-name="redirect:/login" />
    <!-- 對靜態資原始檔的訪問  restful-->
    <mvc:resources mapping="/static/**" location="/static/" cache-period="31556926"/>
    <mvc:resources location="/favicon.ico" mapping="/favicon.ico" />
    <mvc:resources mapping="/webjars/**" location="classpath:/META-INF/resources/webjars/" />
    <mvc:resources mapping="/swagger-ui.html" location="classpath:/META-INF/resources/" />
    <!-- 訪問攔截  -->
    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <mvc:exclude-mapping path="/static/**" />
            <mvc:exclude-mapping path="/favicon.ico"/>
            <mvc:exclude-mapping path="/status"/>
            <mvc:exclude-mapping path="/webjars/**" />
            <mvc:exclude-mapping path="/*/api-docs" />
            <mvc:exclude-mapping path="/swagger-ui.html" />
            <mvc:exclude-mapping path="/configuration/**" />
            <bean class="com.buoumall.common.web.interceptor.EventInterceptor">
                <property name="nextInterceptor">
                    <array>
                        <!-- 登入攔截器 -->
                        <bean class="com.buoumall.front.interceptor.LoginInterceptor">
                            <property name="loginUrl" value="/login"/>
                        </bean>
                    </array>
                </property>
            </bean>
        </mvc:interceptor>
    </mvc:interceptors>

    <!-- 驗證碼 -->
    <bean id="captchaProducer" class="com.google.code.kaptcha.impl.DefaultKaptcha">
        <property name="config">
            <bean class="com.google.code.kaptcha.util.Config">
                <constructor-arg>
                    <props>
                        <prop key="kaptcha.border">no</prop>
                        <prop key="kaptcha.border.color">105,179,90</prop>
                        <prop key="kaptcha.textproducer.font.color">red</prop>
                        <prop key="kaptcha.image.width">250</prop>
                        <prop key="kaptcha.textproducer.font.size">90</prop>
                        <prop key="kaptcha.image.height">120</prop>
                        <prop key="kaptcha.session.key">code</prop>
                        <prop key="kaptcha.textproducer.char.length">4</prop>
                        <prop key="kaptcha.textproducer.font.names">宋體,楷體,微軟雅黑</prop>
                    </props>
                </constructor-arg>
            </bean>
        </property>
    </bean>
3.生成驗證碼的controller
	//生成驗證碼
	@RequestMapping("/captcha-image")
	public void captchaImageCode(@RequestParam Integer bizType,HttpServletRequest request, HttpServletResponse response) throws BizException {
		response.setDateHeader("Expires", 0);
		// Set standard HTTP/1.1 no-cache headers.
		response.setHeader("Cache-Control","no-store, no-cache, must-revalidate");
		// Set IE extended HTTP/1.1 no-cache headers (use addHeader).
		response.addHeader("Cache-Control", "post-check=0, pre-check=0");
		// Set standard HTTP/1.0 no-cache header.
		response.setHeader("Pragma", "no-cache");
		// return a jpeg
		response.setContentType("image/png");
		// create the text for the image
		String capText = captchaProducer.createText();
		//bizType為業務型別,這裡為LOGIN
		BizTypeEnum bizTypeEnum = BizTypeEnum.findByBizType(bizType);
		if (bizTypeEnum == null) {
			logger.error("The bizType is illegal!");
			return;
		}
		//客戶端儲存一個驗證碼的cookie,GlobalConst.BOM_CAPTCHA_KEY為cookie的name,value為一個隨機數
		String captcha_key = CookieUtil.getCookieValue(request,GlobalConst.BOM_CAPTCHA_KEY);
		//如果取出來的為空,這表示是一個新使用者,客戶端沒有這個cookie,就新建一個
		if(StringUtils.isBlank(captcha_key)){
			captcha_key = UuidUtil.get32UUID();
			logger.info("New user coming,captcha_key = {}", captcha_key);
			CookieUtil.addCookie(response, GlobalConst.BOM_CAPTCHA_KEY, captcha_key, CaptchaCodeUtil.CAPTCHA_TIME_OUT);
		}
		//將驗證碼和客戶端cookie的資訊存在redis中
		CaptchaCodeUtil.addCaptchaCode(captcha_key,redisService, bizTypeEnum, capText);
		OutputStream out = null;
		try {
			// create the image with the text
			BufferedImage bi = captchaProducer.createImage(capText);
			out = response.getOutputStream();
			// write the data out
			ImageIO.write(bi, "png", out);
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			IOUtils.closeQuietly(out);
		}
	}

	//判斷驗證碼是否正確
	//一般是先判斷驗證碼是否正確,如果正確再進行登入判斷,不然提示驗證碼錯誤。
	@RequestMapping(value = "/checkCaptchaCode")
	@ResponseBody
	public Result<Boolean> checkCaptchaCode(HttpServletRequest request,
			@RequestParam Integer bizType, @RequestParam String captchaCode) {
		//bizType表示業務型別,這裡是LOGIN
		BizTypeEnum bizTypeEnum = BizTypeEnum.findByBizType(bizType);
		if (bizTypeEnum == null) {
			logger.error("The bizType is illegal!");
			return super.buildBizErrorResult("驗證碼bizType不合法");
		}
		//得到客戶端cookie的value,redis裡的key就是通過這個value生成的
		String captchaKey = CookieUtil.getCookieValue(request, GlobalConst.BOM_CAPTCHA_KEY);
		//在redis中判斷驗證碼是否正確
		boolean captchaCodeOk = CaptchaCodeUtil.captchaCodeOk(captchaKey,redisService, bizTypeEnum, captchaCode);
		if (captchaCodeOk) {
			return super.buildOk(true);
		} else {
			return super.buildBizErrorResult("驗證碼不正確");
		}
	}
4.圖形驗證碼工具類
/**
 * 圖形驗證碼工具類,目前主要是未登入,基於Cookie的方式。使用者已登入的情況,暫時沒有。
 */
public class CaptchaCodeUtil {
	
	private static final Logger logger = LoggerFactory.getLogger(CaptchaCodeUtil.class);
	public static final String CAPTCHA_NAMESPACE = "captcha";
	//驗證碼在redis中儲存的時間
	public static final int CAPTCHA_TIME_OUT = 10 * 60;

	//captcha_key為一個隨機數,captchaCode為驗證碼
	//captchaKey為redis中的key,captchaCode為value

	//新增驗證碼資訊到redis
	public static void addCaptchaCode(String captcha_key,RedisService redisService, BizTypeEnum bizType, String captchaCode) throws BizException {
		String captchaKey = buildCaptchaKey(captcha_key, captchaCode, bizType);
		if (StringUtils.isNotBlank(captchaKey)) {
			redisService.add(captchaKey, captchaCode, CAPTCHA_TIME_OUT, TimeUnit.SECONDS);
		}
	}

	// 驗證某個業務的驗證碼,是否正確
	public static boolean captchaCodeOk(String captcha_key,RedisService redisService, BizTypeEnum bizType, String captchaCode) {
		try {
			String captchaKey = buildCaptchaKey(captcha_key,captchaCode, bizType);
			if(StringUtils.isNotBlank(captchaKey)){
				// 驗證碼
				String captchaCodeRedis = redisService.getString(captchaKey);
				if (StringUtils.equalsIgnoreCase(captchaCodeRedis, captchaCode)) {
					return true;
				}
			}
		} catch (Exception e) {
			logger.error(e.getMessage());
			return false;
		}
		return false;
	}

	//重新構建redis中的key
	private static String buildCaptchaKey(String captcha_key,String captchaCode, BizTypeEnum bizType) {
		if(StringUtils.isBlank(captcha_key)){
			return null;
		}
		String captchaKey = CAPTCHA_NAMESPACE + ":" + bizType.getDir() + ":" + captcha_key + ":" + captchaCode;
		return captchaKey;
	}

	//刪除redis中驗證碼的資訊
	public static void deleteCaptchaCode(String captcha_key,RedisService redisService, String captchaCode, BizTypeEnum bizType) throws BizException {
		String captchaKey = buildCaptchaKey(captcha_key, captchaCode, bizType);
		if (StringUtils.isNotBlank(captchaKey)) {
			redisService.delete(captchaKey);
		}
	}
}
5.登入和登出
	@ApiOperation(value = "登入", produces = MediaType.APPLICATION_JSON_VALUE)
	@RequestMapping(value = "/doLogin",produces = MediaType.APPLICATION_JSON_VALUE)
	@ResponseBody
	public Result<Boolean> doLogin(
			@ApiParam(required = true, value = "登入名") @RequestParam String loginName,
			@ApiParam(required = true, value = "登入密碼") @RequestParam String password, 
			@ApiParam(required = true, value = "驗證碼") @RequestParam String captchaCode,
			HttpServletResponse response, 
			HttpServletRequest request
			){
		try {
			BizTypeEnum bizType = BizTypeEnum.LOGIN;
			//從客戶端cookie中得到redis的value
			String captcha_Key = CookieUtil.getCookieValue(request, GlobalConst.BOM_CAPTCHA_KEY);
			boolean captchaCodeOk = CaptchaCodeUtil.captchaCodeOk(captcha_Key, redisService, bizType, captchaCode);
			if(!captchaCodeOk){
				return buildBizErrorResult("驗證碼錯誤!");
			}
			// 刪除驗證碼
			CaptchaCodeUtil.deleteCaptchaCode(captcha_Key, redisService, captchaCode, bizType);
			Member member = memberSecurityBizService.login(loginName, password);
			if(member == null){
				logger.info("Login failed,loginName=" + loginName);
				return buildBizErrorResult("使用者名稱和密碼資訊輸入有誤!");
			}
			if(!RoleTypeEnum.DESIGNER.getCode().equals(member.getRoleType()) && !RoleTypeEnum.EDITOR.equals(member.getRoleType())){
				return buildBizErrorResult("你無權登入本系統!");
			}
			//使用者token的key值
			String login_ut_key = UuidUtil.get32UUID();
			//在客戶端新增有關使用者資訊的Cookie
			CookieUtil.addCookie(response, GlobalConst.BOM_FRONT_UT, login_ut_key, GlobalConst.KEEP_BOM_FRONT_UT_TIME);
			//在redis中新增使用者資訊
			redisService.add(login_ut_key, member,1,TimeUnit.HOURS);
		    return buildOk(true);
		} catch (Exception e) {
			return buildExceptionResult(e);
		}
	}

	@ApiOperation(value = "登出", produces = MediaType.TEXT_HTML_VALUE)
	@RequestMapping(value = "/logout", produces = MediaType.TEXT_HTML_VALUE)
	public String logout(HttpServletRequest request, HttpServletResponse response) {
		try {
			logger.info("Logout");
			String accessTokenKey = CookieUtil.getCookieValue(request,GlobalConst.BOM_FRONT_UT);
			if(StringUtils.isNotBlank(accessTokenKey)){
				CookieUtil.removeCookie(response, GlobalConst.BOM_FRONT_UT);
				redisService.delete(accessTokenKey);
			}
		} catch (Exception e) {
			logger.error("Logout failed");
			e.printStackTrace();
		}
		return "login/login";
	}
6.登入攔截器LoginInterceptor
package com.front.interceptor;

import java.io.IOException;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.method.HandlerMethod;

import com.alibaba.fastjson.JSON;
import com.common.web.interceptor.BaseInterceptor;
import com.common.web.util.CommonUtil;
import com.common.web.util.CookieUtil;
import com.common.web.util.RequestUtils;
import com.common.web.util.ResponseUtils;
import com.commonService.service.RedisService;
import com.front.result.Result.ResultCodeEnum;
import com.front.util.FrontLoginUtil;
import com.front.util.GlobalConst;
import com.front.util.NeedRole;
import com.userService.enums.RoleTypeEnum;
import com.userService.model.Member;
import com.userService.model.MemberDetail;
import com.userService.service.MemberDetailService;
import com.google.common.collect.Maps;

public class LoginInterceptor extends BaseInterceptor {

	private String loginUrl;

	private Logger logger = LoggerFactory.getLogger(getClass());

	@Autowired
	private MemberDetailService memberDetailService;
	@Autowired
	private RedisService redisService;
	
	@Override
	public boolean preHandle(HttpServletRequest request,HttpServletResponse response, Object handler) throws Exception {
		//將執行緒中的使用者資訊清空
		FrontLoginUtil.setCurrentUser(null);
		// User專案也需要響應圖片
		boolean b = handler instanceof HandlerMethod;
		if (!b) {
			return true;
		}
		//客戶端cookie使用者的value
		String accessToken = CookieUtil.getCookieValue(request,GlobalConst.BOM_FRONT_UT);
		
		Member member =null;
		try {
			if(StringUtils.isNotEmpty(accessToken)){
				member = (Member) redisService.getValue(accessToken);
			}
		} catch (Exception e) {
			logger.info(e.getMessage());
			CookieUtil.removeCookie(response, GlobalConst.BOM_FRONT_UT);
		}
		boolean exist = CommonUtil.hasNeedLogin(handler);
		if (!exist) {
			return true;
		}
		boolean isAjaxRequest = RequestUtils.isAjaxRequest(request);
		// 需要登入的
		if (member != null) {
			//重新整理redis中使用者資訊儲存的時間,只要操作就不會掉線
			redisService.expire(accessToken, 1, TimeUnit.HOURS);
			if(!RoleTypeEnum.DESIGNER.getCode().equals(member.getRoleType()) && !RoleTypeEnum.EDITOR.equals(member.getRoleType())){
				logger.error("你無權操作本系統!");
				jump(response, isAjaxRequest);
				return false;
			}
			MemberDetail detail = memberDetailService.getByMemberId(member.getId());
			//儲存使用者資訊
			FrontLoginUtil.initCurrentUser(member, detail);
			//判斷“角色”
			NeedRole needRole=hasNeedRole(handler);
			if(needRole==null){
				return true;
			}else{
				Integer roleType = member.getRoleType();
				boolean needDesigner=needRole.designer();
				boolean needEditor=needRole.editor();
				//需要設計師許可權,但是沒有
				if(needDesigner && !RoleTypeEnum.DESIGNER.getCode().equals(roleType)){
					logger.error("許可權不足,沒有設計師許可權");
					jump(response, isAjaxRequest);
					return false;
				}
				//需要編輯許可權,但是沒有
				if(needEditor && !RoleTypeEnum.EDITOR.getCode().equals(roleType)){
					logger.error("許可權不足,沒有服務者許可權");
					jump(response, isAjaxRequest);
					return false;
				}
			}
			return true;
		} else {
			jump(response, isAjaxRequest);
			return false;
		}
	}

	// 需要登入,但是沒有登入
	private void jump(HttpServletResponse response, boolean isAjaxRequest) throws IOException {
		if (isAjaxRequest) {
			// 非同步
			jsonLogin(response);
		}else {
			// 同步
			response.sendRedirect(loginUrl);
		}
	}
	
	private static void jsonLogin(HttpServletResponse response) {
		Map<String, Object> map = Maps.newHashMap();
		map.put("code", ResultCodeEnum.NEED_LOGIN.getCode());
		map.put("msg", "會話超時,請重新登入");
		ResponseUtils.renderJson(response, JSON.toJSONString(map));
	}
	
	public String getLoginUrl() {
		return loginUrl;
	}

	public void setLoginUrl(String loginCenterUrl) {
		this.loginUrl = loginCenterUrl;
	}
	
	public static NeedRole hasNeedRole(Object handler) {
		if(!(handler instanceof HandlerMethod)){
			return null;
		}
		HandlerMethod handlerMethod = (HandlerMethod) handler;
		Class<?> type = handlerMethod.getBeanType();
	
		// 判斷是否有NeedRole註解
		NeedRole needRole = type.getAnnotation(NeedRole.class);
		if (needRole == null) {
			needRole = type.getSuperclass().getAnnotation(NeedRole.class);
		}
		if (needRole == null) {
			needRole = handlerMethod.getMethodAnnotation(NeedRole.class);
		}
		return needRole;
	}
}
7.前端用的登入工具,儲存使用者資訊
package com.front.util;

import com.front.model.UserInfo;
import com.userService.model.Member;
import com.userService.model.MemberDetail;

public class FrontLoginUtil {

	private static ThreadLocal<UserInfo> currentUser = null;

	private FrontLoginUtil(){
	}
	
	public static void setCurrentUser(UserInfo userInfo) {
		if(currentUser == null){
			currentUser = new ThreadLocal<UserInfo>();
		}
		currentUser.set(userInfo);
	}

	public static UserInfo getCurrentUser() {
		return currentUser.get();
	}

	public static void initCurrentUser(Member member,MemberDetail memberDetail) {
		if (member != null) {
			UserInfo userInfo = new UserInfo();
			userInfo.setId(member.getId());
			userInfo.setName(member.getName());
			userInfo.setMobile(member.getMobile());
			userInfo.setRoleType(member.getRoleType());
			userInfo.setNickname(memberDetail.getNickname());
			userInfo.setAvatar(memberDetail.getAvatar());
			setCurrentUser(userInfo);
		}
	}
}
8.cookie工具類CookieUtil
package com.common.web.util;

import java.util.Map;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.springframework.util.Assert;

import com.google.common.collect.Maps;

public class CookieUtil {
	/**
	 * 寫入一個Cookie
	 * @param response
	 * @param name
	 * @param path
	 * @param value
	 * @param maxAge
	 */
	public static void addCookie(HttpServletResponse response, String name, String value, String path, int maxAge) {       
        Cookie cookie = new Cookie(name, value);
        if(StringUtils.isNotBlank(path)){
        	cookie.setPath(path);	
        }       
        if (maxAge>0) {
        	cookie.setMaxAge(maxAge);
        }
        response.addCookie(cookie);
    }
	
	/**
	 * 寫入一個Cookie
	 * @param response
	 * @param name
	 * @param value
	 * @param maxAge
	 *
	 */
	public static void addCookie(HttpServletResponse response, String name, String value, int maxAge) {       
		addCookie(response, name, value,"/", maxAge);
	}
	
	/**
	 * 根據名稱獲得value
	 * @param request
	 * @param name
	 * @return
	 */
	public static String getCookieValue(HttpServletRequest request, String name) {
		Cookie[] cookies=request.getCookies();
        if(cookies != null){
        	for (Cookie cookie : cookies) {
        		if(cookie.getName().equalsIgnoreCase(name)){
        			return cookie.getValue();
        		}
			}
        }
        return null;
    }
	
	/**
	 * 刪除某個cookie
	 * 
	 * @param response
	 * @param name
	 * @param path
	 */
	public static void removeCookie(HttpServletResponse response,String name,String path) {		
		Cookie cookie = new Cookie(name,null);
		cookie.setMaxAge(0);
		cookie.setPath(path);
		response.addCookie(cookie);		
	}
	
	/**
	 * 刪除某個cookie
	* 
	* @param response
	* @param name
	*
	 */
	public static void removeCookie(HttpServletResponse response,String name) {	
		removeCookie(response, name, "/");
	}
	  
	/**
	 * 讀取全部Cookie到Map
	 * @param request
	 * @return
	 */
	public static Map<String, Cookie> readCookieToMap(HttpServletRequest request) {
        Map<String, Cookie> cookieMap = Maps.newHashMap();
        Cookie[] cookies = request.getCookies();
        if (null != cookies) {
            for (Cookie cookie : cookies) {
              cookieMap.put(cookie.getName(), cookie);
			}
        }
        return cookieMap;
    }
    
    /**
     * 清除所有Cookie
     * @param request
     * @param response
     */
    public static void clear(HttpServletRequest request,HttpServletResponse response) {
		Cookie[] cookies = request.getCookies();
		if(cookies!=null){
			for(Cookie cookie:cookies) {
				cookie.setValue(null);
				cookie.setMaxAge(0);
				response.addCookie(cookie);
			}	
		}		
	}
    
    /**
	 * 獲得cookie
	 * 
	 * @param request
	 *            HttpServletRequest
	 * @param name
	 *            cookie name
	 * @return if exist return cookie, else return null.
	 */
	public static Cookie getCookie(HttpServletRequest request, String name) {
		Assert.notNull(request);
		Cookie[] cookies = request.getCookies();
		if (cookies != null && cookies.length > 0) {
			for (Cookie cookie : cookies) {
				if (cookie.getName().equalsIgnoreCase(name)) {
					return cookie;
				}
			}
		}
		return null;
	}
}
9.redis的工具類

一、介面

package com.commonService.service;

import java.io.Serializable;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

import com.commonService.exception.BizException;

/**
 * 快取資料服務
 */
public interface RedisService {

	/**
	 * 加入快取
	 * 
	 * @param key 加入快取
	 * @param value 快取的資料
	 * @return
	 * @throws BizException 
	 */
	public abstract void add(String key, Serializable value) throws BizException;

	/**
	 * 加入快取,並設定快取時間
	 * 
	 * @param key 快取的key
	 * @param value 快取的值
	 * @param timeout 快取時間
	 * @param unit 快取時間型別列舉
	 * @throws BizException 
	 *
	 */
	public abstract void add(String key, Serializable value, long timeout,
			TimeUnit unit) throws BizException;

	/**
	 * 如果這個key不在快取中就設定
	 * 
	 * @param key 快取的key
	 * @param value 快取的物件
	 * @return true/false
	 * @throws BizException 
	 *
	 */
	public abstract boolean setIfAbsent(String key, Serializable value) throws BizException;

	/**
	 * 獲取快取字串
	 * 
	 * @param key 快取的key
	 * @return 快取的字串
	 * @throws BizException 
	 *
	 */
	public String getString(String key) throws BizException;
	/**
	 * 獲取快取的物件
	 * 
	 * @param key 快取的key
	 * @return 快取的物件
	 * @throws BizException 
	 *
	 */
	public abstract Serializable getValue(String key) throws BizException;

	/**
	 * 獲取指定時間之內redis自增序列
	 * 
	 * @param key 快取的key
	 * @param timeout 時間
	 * @param timeUnit 時間型別
	 * @return 自增值
	 * @throws BizException 
	 *
	 */
	public abstract Long getSequence(String key, long timeout, TimeUnit timeUnit) throws BizException;

	/**
	 * 刪除快取的物件
	 * 
	 * @param key 快取的key
	 * @return
	 * @throws BizException 
	 */
	public abstract void delete(String key) throws BizException;

	/**
	 * 批量刪除快取物件
	 * 
	 * @param keys 快取的key集合
	 * @return
	 * @throws BizException 
	 */
	public abstract void delete(Collection<String> keys) throws BizException;

	/**
	 * 判斷key是否存在
	 * 
	 * @param key 快取的key
	 * @return 是否存在
	 * @throws BizException 
	 */
	public abstract boolean exists(String key) throws BizException;

	/**
	 * 設定指定key的過期時間
	 * 
	 * @param key 引數key
	 * @param timeout 時間
	 * @param unit 時間型別
	 * @return 是否成功
	 * @throws BizException 
	 *
	 */
	public abstract boolean expire(String key, long timeout, TimeUnit unit) throws BizException;

	/**
	 * 設定指定key的過期日期
	 * 
	 * @param key 引數key
	 * @param date 日期
	 * @return 是否成功
	 * @throws BizException 
	 *
	 */
	public abstract boolean expireAt(String key, Date date) throws BizException;

	/**
	 * 獲取key過期剩餘時間,單位是秒
	 * 
	 * @param key 快取的key
	 * @return 時間
	 * @throws BizException 
	 *
	 */
	public abstract Long getExpire(String key) throws BizException;

	/**
	 * 獲取key過期剩餘時間
	 * 
	 * @param key 獲取過期
	 * @param unit 時間型別
	 * @return 時間
	 * @throws BizException 
	 *
	 */
	public abstract Long getExpire(String key, TimeUnit unit) throws BizException;

	/**
	 * 獲取所有匹配的key物件列表
	* 
	* @param pattern 例如  KEYS w3c* 獲取以 w3c 為開頭的 key的值
	* @return 物件列表
	 * @throws BizException 
	*
	 */
	public <T> List<T> getByPattern(String pattern) throws BizException;
}
二、實現類
package com.commonService.service.impl;

import java.io.Serializable;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import org.apache.commons.collections.CollectionUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.util.StringUtils;

import com.commonService.exception.BizException;
import com.commonService.service.RedisService;
import com.google.common.collect.Lists;

public class RedisServiceImpl implements RedisService {

	private RedisTemplate<String, Serializable> redisTemplate;

	public RedisTemplate<String, Serializable> getRedisTemplate() {
		return redisTemplate;
	}

	public void setRedisTemplate(RedisTemplate<String, Serializable> redisTemplate) {
		this.redisTemplate = redisTemplate;
	}

	private String redisKeyNamespace;
	
	public String getRedisKeyNamespace() {
		return redisKeyNamespace;
	}

	public void setRedisKeyNamespace(String redisKeyNamespace) {
		this.redisKeyNamespace = redisKeyNamespace;
	}

	// 加入快取
	@Override
	public void add(String key, Serializable value) throws BizException {
		key = buildKey(key);
		ValueOperations<String, Serializable> valueops = redisTemplate.opsForValue();
		valueops.set(key, value);
	}
	
	// 加入快取,並設定快取時間
	@Override
	public void add(String key, Serializable value, long timeout, TimeUnit unit) throws BizException {
		key = buildKey(key);
		ValueOperations<String, Serializable> valueops = redisTemplate.opsForValue();
		valueops.set(key, value, timeout, unit);
	}

	// 如果這個key不在redis中就設定
	@Override
	public boolean setIfAbsent(String key, Serializable value) throws BizException {
		key = buildKey(key);
		ValueOperations<String, Serializable> valueops = redisTemplate.opsForValue();
		boolean result = valueops.setIfAbsent(key, value);
		return result;
	}

	// 獲取快取的物件
	@Override
	public String getString(String key) throws BizException {
		key = buildKey(key);
		ValueOperations<String, Serializable> valueops = redisTemplate.opsForValue();
		String value =  (String) valueops.get(key);
		return value;
	}
	// 獲取快取的物件
	@Override
	public Serializable getValue(String key) throws BizException {
		key = buildKey(key);
		ValueOperations<String, Serializable> valueops = redisTemplate.opsForValue();
		Serializable value = valueops.get(key);
		return value;
	}
	//獲取所有匹配的key 例如  KEYS w3c* 獲取以 w3c 為開頭的 key 的值
	@Override
	public <T> List<T> getByPattern(String pattern) throws BizException {
		pattern = buildKey(pattern);
		Set<String> keys = redisTemplate.keys(pattern);
		ValueOperations<String, Serializable> valueops = redisTemplate.opsForValue();
		@SuppressWarnings("unchecked")
		List<T> list = (List<T>) valueops.multiGet(keys);
		return list;
	}
	
	// 獲取一段時間自增序列
	@Override
	public Long getSequence(String key, long timeout, TimeUnit timeUnit) throws BizException {
		key = buildKey(key);
		ValueOperations<String, Serializable> valueOperations = redisTemplate.opsForValue();
		Long sequence = null;
		if(redisTemplate.hasKey(key)){
			 sequence = valueOperations.increment(key, 1l);
		}else{
			 sequence = valueOperations.increment(key, 1l);
			 redisTemplate.expire(key, timeout, timeUnit);
		}
		return sequence;
	}

	// 單個刪除
	@Override
	public void delete(String key) throws BizException {
		key = buildKey(key);
		redisTemplate.delete(key);
	}

	@Override
	public void delete(Collection<String> keys) throws BizException {
		if (CollectionUtils.isEmpty(keys)) {
			throw new BizException("key 不能為空!");
		}
		List<String> list = Lists.newArrayList();
		for (String key : keys) {
			list.add(buildKey(key));
		}
		redisTemplate.delete(list);
	}

	// 判斷key是否存在
	@Override
	public boolean exists(String key) throws BizException {
		key = buildKey(key);
		Boolean b = redisTemplate.hasKey(key);
		return b.booleanValue();
	}

	// 設定指定key的過期時間
	@Override
	public boolean expire(String key, long timeout, TimeUnit unit) throws BizException {
		key = buildKey(key);
		return redisTemplate.expire(key, timeout, unit);
	}

	// 設定指定key的過期時間
	@Override
	public boolean expireAt(String key, Date date) throws BizException {
		key = buildKey(key);
		return redisTemplate.expireAt(key, date);
	}

	// 獲取key過期剩餘時間,單位是秒
	@Override
	public Long getExpire(String key) throws BizException {
		key = buildKey(key);
		return redisTemplate.getExpire(key);
	}

	// 獲取key過期剩餘時間
	@Override
	public Long getExpire(String key, TimeUnit unit) throws BizException {
		key = buildKey(key);
		return redisTemplate.getExpire(key, unit);
	}
	
	private String buildKey(String key) throws BizException {
		if (StringUtils.isEmpty(key)) {
			throw new BizException("key 不能為空!");
		}
		if (StringUtils.hasText(redisKeyNamespace)) {
			key = redisKeyNamespace + ":" + key;
		}
		return key;
	}
}

kaptcha驗證碼元件的使用可以參考http://chenzhou123520.iteye.com/blog/1987636

http://www.open-open.com/lib/view/open1395238908947.html





相關推薦

分散式環境登入應用

在分散式環境中使用kapcha和redis完成登入功能。 1.新增jar包 <!-- kaptcha --> <dependency> <groupId>com.google.code.kaptcha</groupI

初識LVS(三)——DR工作模式實際環境應用

lvs dr 背景介紹LVS的NAT模型所有的請求和響應報文會經過director,此時director就成為了性能瓶頸,而為了解決這個問題LVS也默認采用的是DR模型(下圖),請求報文(藍色)向director發出請求,響應報文(紅色)由RS服務器直接返還給用戶不經由director,通常RS的網關

百曉生詳解nginx(下)nginx在實際生產環境應用(該帖連載更新)

2.0 編譯環境 + - apache 80端口 ali oss keep fff 案例一:Nginx+Tomcat負載均衡集群的部署案例概述:京北點指科技有限公司發布V3版移聯建站管理系統,該項目為JAVA語言開發web站點。實驗環境:站點服務器(tomcat1)cent

uvm_config_db在UVM驗證環境應用

本文轉自:http://www.eetop.cn/blog/html/28/1561828-437621.html 如何在有效的使用uvm_config_db來搭建我們的uvm驗證環境對於許多驗證團隊來說仍然是一個挑戰,一些驗證團隊完全避免使用它,這樣就不能夠有效利用它帶來的好處,另一些驗證團隊

Zookeeper和分散式環境的假死腦裂問題

最近和同事聊天無意間發現他們的系統也存在腦裂的問題。想想當初在我們的系統中為了解決腦裂花了非常大的功夫,現在和大家一起討論下腦裂,假死等等這些問題和解決的方法。 在一個大叢集中往往會有一個master存在,在長期執行過程中不可避免的會出現宕機等問題導致master不可用,在出現這樣的情況以後往往

審計日誌在分散式系統應用

前言分散式系統的執行環境往往是異常複雜的,很多情況涉及到多節點間的訊息通訊。相比較於單節點系統而言,分散式系統在問題追蹤,排查方面顯然也複雜很多。那麼這個時候,在分散式系統中,增加哪些型別的日誌資料,來幫助我們發現和定位問題呢?答案就是我們今天將要闡述的審計日誌(Audit log)。 審計日誌的概念很多人

ZooKeeper學習第五期--ZooKeeper管理分散式環境的資料

引言 本節本來是要介紹ZooKeeper的實現原理,但是ZooKeeper的原理比較複雜,它涉及到了paxos演算法、Zab協議、通訊協議等相關知 識,理解起來比較抽象所以還需要藉助一些應用場景,來幫我們理解。由於內容比較多,一口氣吃不成胖子,得慢慢來一步一個腳印,因此我對後期 ZooKeeper的學習規

Zookeeper的簡單原理介紹以及分散式服務應用,都是乾貨!!!

安裝和配置詳解 本文介紹的 Zookeeper 是以 3.2.2 這個穩定版本為基礎,最新的版本可以通過官網 http://hadoop.apache.org/zookeeper/來獲取,Zookeeper 的安裝非常簡單,下面將從單機模式和叢集模式兩個方面介紹 Zooke

一致性雜湊演算法原理及其在分散式系統應用

分散式快取問題 假設我們有一個網站,最近發現隨著流量增加,伺服器壓力越來越大,之前直接讀寫資料庫的方式不太給力了,於是我們想引入Memcached作為快取機制。現在我們一共有三臺機器可以作為Memcached伺服器,如下圖所示。 很顯然,最簡單的策略是將每一次Memcached請求隨機發送到一臺Memca

徹頭徹尾理解單例模式及其在多執行緒環境應用

摘要:      本文首先概述了單例模式產生動機,揭示了單例模式的本質和應用場景。緊接著,我們給出了單例模式在單執行緒環境下的兩種經典實現:餓漢式 和懶漢式,但是餓漢式是執行緒安全的,而懶漢式是非執行緒安全的。在多執行緒環境下,我們特別介紹了五種方式來在多執行緒環境下建立執行緒安全的單例,即分別使用sy

一致性雜湊演算法在分散式場景應用

文章概要 本文將會從實際應用場景出發,介紹一致性雜湊演算法(Consistent Hashing)及其在分散式系統中的應用。首先本文會描述一個在日常開發中經常會遇到的問題場景,藉此介紹一致性雜湊演算法以及這個演算法如何解決此問題;接下來會對這個演算法進行相對詳細的描述,並討論一些如虛擬節

JWT學習(二):JWT在分散式SSO應用例項

上一篇文章講解了JWT的基本簡介,這一篇文章我就來實戰一下。介紹一下在分散式單點登入中的使用方法: 首先來看一下Token實體類, public class Token implements Serializable{ private static final lo

Kafka在大數據環境應用

rri 開發 iter pack success 應用 引入 class 支持 我們生活在一個數據爆炸的時代,數據的巨量增長給我們的業務處理帶來了壓力,同時巨量的數據也給我們帶來了十分可觀的財富。隨著大數據將各個行業用戶、運營商、服務商的數據整合進大數據環境,或用戶取用大數

ZooKeeper學習第五期–ZooKeeper管理分散式環境的資料

ZooKeeper學習第五期–ZooKeeper管理分散式環境中的資料 引言 本節本來是要介紹ZooKeeper的實現原理,但是ZooKeeper的原理比較複雜,它涉及到了paxos演算法、Zab協議、通訊協議等相關知識,理解起來比較抽

Kafka在大資料環境應用

我們生活在一個數據爆炸的時代,資料的巨量增長給我們的業務處理帶來了壓力,同時巨量的資料也給我們帶來了十分可觀的財富。隨著大資料將各個行業使用者、運營商、服務商的資料整合進大資料環境,或使用者取用大資料環境中海量的資料,業務平臺間的訊息處理將變得尤為複雜。如何高效地採集、使用資料,如何減輕各業

系統技術非業餘研究 » 區域性性原理在計算機和分散式系統應用課程PPT

這個課程最主要focus在資料的區域性性原理,從硬體到作業系統到應用程式這樣的順序過來的,對於我們提高核心系統軟體的效能非常有啟發意義. 課件下載點選這裡 修正:由於原連結已經不存在了,特地在這裡放了一份。 以下是教程的介紹: 課程簡介 ___________________________

分散式環境如何解決session共享問題

一、什麼是session session在計算機中,尤其是在網路應用中,稱為”會話控制“。Session物件儲存特定使用者會話所需的屬性及配置資訊。這樣,當用戶在應用程式的web頁面之間跳轉時,儲存在session物件中的變數將不會丟失,而在整個使用者會話中一直存在下去。

Zookeeper已經分散式環境的假死腦裂

Zookeeper簡介 在上班之前都不知道有這樣一個東西,在開始說假死腦裂之前先說說Zookeeper吧。 Zookeeper zookeeper是一個分散式應用程式的協調服務。它是一個為分散式應用提供一致性服務的軟體,提供的效能包括:配置維護、名字服

Zookeeper--分散式服務框架 Zookeeper -- 管理分散式環境的資料

Zookeeper 分散式服務框架是 Apache Hadoop 的一個子專案,它主要是用來解決分散式應用中經常遇到的一些資料管理問題,如:統一命名服務、狀態同步服務、叢集管理、分散式應用配置項的管理等。本文將從使用者角度詳細介紹 Zookeeper 的安裝和配置檔案中

關於hbase分散式環境遇到的問題---HMaster 啟動後自動關閉

最近在搭建hbase分散式環境中遇到的問題,出現這個問題我也去網上參考別人是怎麼解決的,但是沒有解決我的問題,我不斷的去看日誌為什麼會出現這個,後來我發現日誌不斷提示異常,我就想是不是和這個有關係,嘗試了一下,解決了我的問題,話不多說,先上日誌:2018-03-26 21:3