单点登录(SSO)
原文地址:从该网站摘抄
SSO是指在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。它包括可以将这次主要的登录映射到其他应用中用于同一用户的登录的机制。
SSO的实现过程
通过上述图形,我们可以看到SSO的答题实现步骤主要分为两大步:存储登录信息,查验登录信息。
SSO分类
对于SSO,我们也可以将之分为两大不同的类型:同域SSO和跨域SSO;
其中同域SSO又可以分为完全同域SSO和同父域SSO。
完全同域SSO
值得是域名完全相同的多个应用系统中实现单点登录。
其实现步骤主要分为:前期准备工作,编写统一登录接口,编写登录校验接口,编写验证页面,实现SSO。
1、编写统一登录接口
登录页面的编写主要是登入用户的登录信息,包括用户名,密码,以及登录页面的地址。因为存在多个应用系统,所以用户在统一登录页面登录成功后,系统需要知道用户想要访问的是哪个系统页面,这个时候就需要记录用户第一次访问的地址,等到用户登录成功后,就可以直接跳转该页面。
<body>
<center>
<h1>请登录</h1>
<form action="/sso/doLogin.action" method="POST">
<span>用户名:</span><input type="text" name="username" />
<span>密码:</span><input type="text" name="password" />
//暂存需要登录页面的url地址
<input type="hidden" name="gotoUrl" value="${gotoUrl}" />
<input type="submit" />
</form>
</center>
</body>
2、登录方法的编写
需要新建cookie,将用户的信息存进cookie中,并指定cookie的路径,因为是完全同域,所以cookie的地址可以简写(“/”)。这里必须要设置cookie的路径,如果不设置,那么cookie的路径将并不一定在当前域名的顶层,它有可能就在当前的这个路径下才可见,这样会导致在当前域的其他路径下找不到这个cookie,解决办法就是把cookie设置到当前域的最顶层域里面,这样当前域下的所有应用就会都可见。
public String doLogin(){
//新建Cookie
Cookie cookie = new Cookie("ssocookie","sso");
//设置Cookie路径
cookie.setPath("/");
HttpServletResponse response = ServletActionContext.getResponse();
response.addCookie(cookie);
return "success";
}
3、登录校验接口的编写
通常SSO登录接口校验都会放在登录拦截器中,当用户想要访问某个系统时,登录拦截器将直接重定向到统一登录页面,用户填写完登录信息后,就会进行登录校验。
//cookie校验(放在拦截器中进行校验)
public static boolean checkCookie(HttpServletRequst request){
Cookie[] cookies=request.getCookies();
if(cookies!=null){
for(Cookies cookie:cookies){
if(cookie.getName().equals("ssocookie") && cookie.getValue().equals("sso")){
return true;
}
}
}
return false;
}
4、编写测试主页
public String main(){
HttpServletRequst request = ServletActionContext.getRequst();
if(SSOCheck.checkCookie(request)){
//登陆成功,记录用户登录信息......
return "success";
}
//登录失败,暂存需要访问的地址,登录成功后,直接访问该地址
gotoUrl="/demo1/main.action";
return "login";
}
5、最后struts2的配置文件
<struts>
<package name="sso" namespace="/sso" extends="struts-default">
<action name="doLogin" class="com.xm.controllerAction.SSOAction" method="doLogin">
<!-- 用户登录成功后,需要进行重定向,重新跳转到用户最初访问的路径 -->
<result name="success" type="redirect">${gotoUrl}</result>
</action>
</package>
<package name="demo1" namespace="/demo1" extends="struts-default">
<action name="main" class="com.xm.controllerAction.Demo1Action" method="main">
<result name="success">/success1.jsp</result>
<result name="login">/login.jsp</result>
</action>
</package>
<package name="demo2" namespace="/demo2" extends="struts-default">
<action name="main" class="com.xm.controllerAction.Demo2Action" method="main">
<result name="success">/success2.jsp</result>
<result name="login">/login.jsp</result>
</action>
</package>
</struts>
同父域SSO
指的是父域名相同的应用系统上实现SSO。
其实现步骤与上述完全同域SSO相同。
其中检验域名:http://check.x.com
测试页面域名:http://demo1.x.com和http://demo2.x.com
1、编写统一登录接口
代码实现与完全同域名SSO基本一致,不过在设置提交路径时,因为二级域名不同,所以不能写成相对路径,需要写成绝对路径地址。
<body>
<center>
<h1>请登录</h1>
//表单提交地址需写成绝对路径,不能写相对路径
<form action="http://check.x.com/sso/doLogin.action" method="POST">
<span>用户名:</span><input type="text" name="username" />
<span>密码:</span><input type="text" name="password" />
//暂存需要登录页面的url地址
<input type="hidden" name="gotoUrl" value="${gotoUrl}" />
<input type="submit" />
</form>
</center>
</body>
2、登录方法
在设置cookie路径的时候有所变化,同上,为了使得当前两个同父域的应用系统都可见这个cookie,那么我们需要将这个cookie设置到父域下面,而不应该设置到本域下面,这样才可以实现域不同,但是父域相同的应用都可以看到的这个cookie。
public String doLogin(){
boolean ok = SSOCheck.checkLogin(userName,passWord);
if(ok){
//新建Cookie
Cookie cookie = new Cookie("ssocookie","sso");
//设置Cookie的父域
cookie.setDomain(".x.com");
//设置Cookie路径
cookie.setPath("/");
HttpServletResponse response = ServletActionContext.getResponse();
response.addCookie(cookie);
return "success";
}
}
3、登录校验接口
因为有着不同的域,所以我们应该将登录所获得cookie传到专门的校验域下的校验方法中进行校验,否则我们需要在各自不同的登录页面实现校验,这样显得代码十分的冗余。
private String cookieName;
private String cookieValue;
public String getCookieName() {
return cookieName;
}
public void setCookieName(String cookieName) {
this.cookieName = cookieName;
}
public String getCookieValue() {
return cookieValue;
}
public void setCookieValue(String cookieValue) {
this.cookieValue = cookieValue;
}
//二级域名向二级域名发送请求
public void checkCookie() throws IOException{
boolean ok=SSOCheck.checkCookie(cookieName,cookieValue);
String result="0";
if(ok){
result="1";
}
HttpServletResponse response = ServletActionContext.getResponse();
response.getWriter().print(result);
response.getWriter().close();
}
public static boolean checkCookie(String cookieName,String cookieValue){
if(cookieName.equals("ssocookie")&&cookieValue.equals("sso")){
return true;
}
return false;
}
4、编写测试主页
public String main(){
HttpServletRequst request = ServletActionContext.getRequst();
//获取cookie
Cookie[] cookies=request.getCookies();
if(cookies!=null){
for(Cookie cookie:cookies){
if(cookie.getName().equals("ssocookie")){
//向检验服务器中发送cookieName和cookieValue
String result = Demo1Tool.doGet("http://check.x.com/so/checkCookie.action",
cookie.getName(),cookie.getValue());
if(result.equals("1")){
return "success";
}
}
}
}
//暂存需要访问的地址,登录成功后,直接访问该地址
gotoUrl="http://demo1.x.com/demo1/main.action";
return "login";
}
public static String doGet(String url,String cookieName,String cookieValue){
//定义返回值
StringBuffer sb = new StringBuffer();
HttpURLConnection httpURLConnection = null;
try{
//校验方法所在的地址
URL urls = new URL(url+"?cookieName="+cookieName+"&cookieValue="+cookieValue);
//打开连接
httpURLConnection = (HttpURLConnection) urls.openConnection();
//设置打开连接的方法
httpURLConnection.setRequestMethod("GET");
//开始连接
httpURLConnection.connect();
InputStream in = httpURLConnection.getInputStream();
InputStreamReader isr = new InputStreamReader(in);
BufferedReader br = new BufferedReader(isr);
String temp = null;
while((temp = br.readLine())!=null){
sb.append(temp);
}
br.close();
isr.close();
in.close();
} catch(IOException e){
e.printStackTrace();
} finally{
if(httpURLConnection!=null){
httpURLConnection.disconnect();
}
}
return sb.toString();
}
5、struts配置文件
<struts>
<package name="sso" namespace="/sso" extends="struts-default">
<action name="doLogin" class="com.xm.controllerAction.SSOAction" method="doLogin">
<result name="success" type="redirect">${gotoUrl}</result>
</action>
<action name="checkCookie" class="check.x.com.SSOAction" method="checkCookie">
</action>
</package>
<package name="demo1" namespace="/demo1" extends="struts-default">
<action name="main" class="demo1.x.com.Demo1Action" method="main">
<result name="success">/success1.jsp</result>
<result name="login">/login.jsp</result>
</action>
</package>
<package name="demo2" namespace="/demo2" extends="struts-default">
<action name="main" class="demo2.x.com.Demo2Action" method="main">
<result name="success">/success2.jsp</result>
<result name="login">/login.jsp</result>
</action>
</package>
</struts>
跨域SSO
在域名完全不同的应用程序上实现SSO。
其实现步骤与完全同域SSO相同。
其中校验域名:http://www.x.com
测试页面域名:http://www.a.com和http://www.b.com
1、编写统一登录接口
<body>
<center>
<h1>请登录</h1>
//这里需要向当前所在的域提交申请,因为如果向校验域提交申请,那么在本域中将无法看到cookie
<form action="/${path}/doLogin.action" method="POST">
<span>用户名:</span><input type="text" name="username" />
<span>密码:</span><input type="text" name="password" />
//暂存需要登录页面的url地址
<input type="hidden" name="gotoUrl" value="${gotoUrl}" />
<input type="submit" />
</form>
</center>
</body>
2、登录方法
public String doLogin(){
Map<String,String> map = new HashMap<String,String>();
map.put("userName", userName);
map.put("password", passWord);
String result = Demo1Tool.doGet("http://www.x.com/sso/doLogin.action",map);
if(result.equals("1")){
return "success";
}
return "login";
}
public void doLogin() throw IOException{
boolean ok=SSOCheck.checkCookie(cookieName,cookieValue);
String result = "0";
if(ok){
result = "1";
}
HttpServletResponse response = ServletActionContext.getResponse();
response.getWriter().print(result);
response.getWriter().close();
}
3、登录校验接口
和同父域SSO的登录校验基本一致,没有什么变化。
public void checkCookie() throws IOException{
boolean ok=SSOCheck.checkCookie(cookieName,cookieValue);
String result="0";
if(ok){
result="1";
}
HttpServletResponse response = ServletActionContext.getResponse();
response.getWriter().print(result);
response.getWriter().close();
}
4、编写测试主页
public String main(){
HttpServletRequst request = ServletActionContext.getRequst();
Cookie[] cookies=request.getCookies();
if(cookies!=null){
for(Cookie cookie:cookies){
if(cookie.getName().equals("ssocookie")){
Map<String,String> map = new HashMap<String,String>();
map.put("userName", cookie.getName());
map.put("password", cookie.getValue());
String result = Demo1Tool.doGet("http://www.x.com/so/checkCookie.action",
cookie.getName(),cookie.getValue());
if(result.equals("1")){
return "success";
}
}
}
}
//暂存需要访问的地址,登录成功后,直接访问该地址
path = "demo1";
gotoUrl="http://www.a.com/demo1/main.action";
return "login";
}
public static String doGet(String url,Map<String,String> map){
//定义返回值
StringBuffer sb = new StringBuffer();
HttpURLConnection httpURLConnection = null;
try{
StringBuffer t_s = new StringBuffer(url).append("?");
for(Map.Entry<String, String> entry:map.entrySet()){
t_s.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
}
url = t_s.substring(0,t_s.length()-1);
//校验方法所在的地址
URL urls = new URL(url);
//打开连接
httpURLConnection = (HttpURLConnection) urls.openConnection();
//设置打开连接的方法
httpURLConnection.setRequestMethod("GET");
//开始连接
httpURLConnection.connect();
InputStream in = httpURLConnection.getInputStream();
InputStreamReader isr = new InputStreamReader(in);
BufferedReader br = new BufferedReader(isr);
String temp = null;
while((temp = br.readLine())!=null){
sb.append(temp);
}
br.close();
isr.close();
in.close();
} catch(IOException e){
e.printStackTrace();
} finally{
if(httpURLConnection!=null){
httpURLConnection.disconnect();
}
}
return sb.toString();
}
最重要部分
至此,准备工作做完,接下来是跨域SSO实现中最重要的部分,也就是cookie的设置,在之前完全同域和同父域的情况下,为了实现SSO,我们在进行doLogin是就设置了cookie,因为域名相同,所以十分简单,但是在跨域SSO中,因为不同域之间的cookie是不可见的,所以我们不可能只设置一个cookie,然后令所有的域名下的应用程序皆可见,所以我们应该在每个域下面都有着为本域设置cookie的方法,而不应该直接将设置cookie交给校验域。
//为本域设置cookie的方法
public void addCookie(){
Cookie cookie = new Cookie("ssocookie","sso");
cookie.setPath("/");
HttpServletResponse response = ServletActionContext.getResponse();
response.addCookie(cookie);
}
还需要在配置文件中进行配置
<action name="addCookie" class="www.a.com.Demo1Action" method="addCookie"></action>
进行调用
写完好方法,则需要进行调用,因此我们需要找一个可以让二者进行交会的地方,在这里选择登录成功的瞬间,通过隐藏的Iframe让二者进行交会。
public String doLogin(){
Map<String,String> map = new HashMap<String,String>();
map.put("userName", userName);
map.put("password", passWord);
String result = Demo1Tool.doGet("http://www.x.com/sso/doLogin.action",map);
if(result.equals("1")){
return "success";
}
List hidderUrl = new ArrayList<String>();
hidderUrl.add("http://www.a.com/demo1/addCookie.action");
hidderUrl.add("http://www.b.com/demo2/addCookie.action");
return "login";
}
public String doLogin(){
Map<String,String> map = new HashMap<String,String>();
map.put("userName", userName);
map.put("password", passWord);
String result = Demo1Tool.doGet("http://www.x.com/sso/doLogin.action",map);
if(result.equals("1")){
return "success";
}
List hidderUrl = new ArrayList<String>();
hidderUrl.add("http://www.a.com/demo1/addCookie.action");
hidderUrl.add("http://www.b.com/demo2/addCookie.action");
return "login";
}
<c:forEach var="url" item="${hiddenUrl}">
<iframe src="${url}" width="0px" heigth="0px" style="display:none"></iframe>
</c:forEach>
本人的demo
以下是本人的demo,并未完全实现,仅供参考,也不一定正确,有错误的地方欢迎指正,邮箱:
使用的是SpringCloud,单点登录功能在login模块中写的
pom.xml
<!--单点登录问题-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
util包下两个Java文件
CookieUtils
package com.work.util;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
public final class CookieUtils {
/**
* 得到Cookie的值,不编码
* @param request
* @param cookieName
* @return
*/
public static String getCookieValue(HttpServletRequest request, String cookieName){
return getCookieValue(request, cookieName, false);
}
/**
* 得到Cookie的值,
* @param request
* @param cookieName
* @return
*/
private static String getCookieValue(HttpServletRequest request, String cookieName, boolean isDecoder) {
Cookie[] cookies = request.getCookies();
if (cookies == null || cookieName == null){
return null;
}
String retValue = null;
try {
for (int i = 0; i < cookies.length; i++){
if (cookies[i].getName().equals(cookieName)){
if (isDecoder){
retValue = URLDecoder.decode(cookies[i].getValue(), "UTF-8");
}else {
retValue = cookies[i].getValue();
}
break;
}
}
}catch (UnsupportedEncodingException e){
e.printStackTrace();
}
return retValue;
}
/**
* 得到Cookie的值,
* @param request
* @param cookieName
* @return
*/
public static String getCookieValue(HttpServletRequest request, String cookieName, String encodeString) {
Cookie[] cookieList = request.getCookies();
if (cookieList == null || cookieName == null) {
return null;
}
String retValue = null;
try {
for (int i = 0; i < cookieList.length; i++) {
if (cookieList[i].getName().equals(cookieName)) {
retValue = URLDecoder.decode(cookieList[i].getValue(), encodeString);
break;
}
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return retValue;
}
/**
* 设置Cookie的值 不设置生效时间默认浏览器关闭即失效,也不编码
*/
public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
String cookieValue) {
setCookie(request, response, cookieName, cookieValue, -1);
}
/**
* 设置Cookie的值 在指定时间内生效,但不编码
*/
public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
String cookieValue, int cookieMaxage) {
setCookie(request, response, cookieName, cookieValue, cookieMaxage, false);
}
/**
* 设置Cookie的值 不设置生效时间,但编码
*/
public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
String cookieValue, boolean isEncode) {
setCookie(request, response, cookieName, cookieValue, -1, isEncode);
}
/**
* 设置Cookie的值 在指定时间内生效, 编码参数
*/
public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
String cookieValue, int cookieMaxage, boolean isEncode) {
doSetCookie(request, response, cookieName, cookieValue, cookieMaxage, isEncode);
}
/**
* 设置Cookie的值 在指定时间内生效, 编码参数(指定编码)
*/
public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
String cookieValue, int cookieMaxage, String encodeString) {
doSetCookie(request, response, cookieName, cookieValue, cookieMaxage, encodeString);
}
/**
* 删除Cookie带cookie域名
*/
public static void deleteCookie(HttpServletRequest request, HttpServletResponse response,
String cookieName) {
doSetCookie(request, response, cookieName, "", -1, false);
}
/**
* 设置Cookie的值,并使其在指定时间内生效
* @param cookieMaxage cookie生效的最大秒数
*/
private static final void doSetCookie(HttpServletRequest request, HttpServletResponse response,
String cookieName, String cookieValue, int cookieMaxage, boolean isEncode) {
try {
if (cookieValue == null) {
cookieValue = "";
} else if (isEncode) {
cookieValue = URLEncoder.encode(cookieValue, "utf-8");
}
Cookie cookie = new Cookie(cookieName, cookieValue);
if (cookieMaxage > 0)
cookie.setMaxAge(cookieMaxage);
if (null != request) {// 设置域名的cookie
String domainName = getDomainName(request);
System.out.println(domainName);
if (!"localhost".equals(domainName)) {
cookie.setDomain(domainName);
}
}
cookie.setPath("/");
response.addCookie(cookie);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 设置Cookie的值,并使其在指定时间内生效
* @param cookieMaxage cookie生效的最大秒数
*/
private static final void doSetCookie(HttpServletRequest request, HttpServletResponse response,
String cookieName, String cookieValue, int cookieMaxage, String encodeString) {
try {
if (cookieValue == null) {
cookieValue = "";
} else {
cookieValue = URLEncoder.encode(cookieValue, encodeString);
}
Cookie cookie = new Cookie(cookieName, cookieValue);
if (cookieMaxage > 0)
cookie.setMaxAge(cookieMaxage);
if (null != request) {// 设置域名的cookie
String domainName = getDomainName(request);
System.out.println(domainName);
if (!"localhost".equals(domainName)) {
cookie.setDomain(domainName);
}
}
cookie.setPath("/");
response.addCookie(cookie);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 得到cookie的域名
*/
private static final String getDomainName(HttpServletRequest request) {
String domainName = null;
String serverName = request.getRequestURL().toString();
if (serverName == null || serverName.equals("")) {
domainName = "";
} else {
serverName = serverName.toLowerCase();
serverName = serverName.substring(7);
final int end = serverName.indexOf("/");
serverName = serverName.substring(0, end);
final String[] domains = serverName.split("\\.");
int len = domains.length;
if (len > 3) {
// www.xxx.com.cn
domainName = "." + domains[len - 3] + "." + domains[len - 2] + "." + domains[len - 1];
} else if (len <= 3 && len > 1) {
// xxx.com or xxx.cn
domainName = "." + domains[len - 2] + "." + domains[len - 1];
} else {
domainName = serverName;
}
}
if (domainName != null && domainName.indexOf(":") > 0) {
String[] ary = domainName.split("\\:");
domainName = ary[0];
}
return domainName;
}
}
RedisUtils
package com.work.util;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@Component
public class RedisUtils {
@Autowired
private RedisTemplate redisTemplate;
public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate){
this.redisTemplate = redisTemplate;
}
/*==========common==========*/
/**
* 指定缓存失效时间
* @param key 键
* @param time 时间(秒)
* @return
*/
public 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 long getExpire(String key){
return redisTemplate.getExpire(key, TimeUnit.SECONDS);
}
/**
* 判断key是否存在
* @param key 键
* @return true存在,false不存在
*/
public boolean hasKey(String key){
try {
return redisTemplate.hasKey(key);
}catch (Exception e){
e.printStackTrace();
return false;
}
}
/**
* 删除缓存
* @SuppressWarnings("unchecked") 告诉编译器忽略指定的警告,不用在编译完成后出现警告信息。
* @param key 可以传一个或多个值
*/
@SuppressWarnings("unchecked")
public void del(String ... key){
if (key != null && key.length > 0){
if (key.length == 1){
redisTemplate.delete(key[0]);
}else {
redisTemplate.delete(CollectionUtils.arrayToList(key));
}
}
}
/*==========String==========*/
/**
* 普通缓存获取
* @param key 键
* @return 值
*/
public Object get(String key){
return key == null ? null : redisTemplate.opsForValue().get(key);
}
/**
* 普通缓存放入
* @param key 键
* @param value 值
* @return true成功,false失败
*/
public 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,如果小于等于0,将设置成无限期
* @return true成功,false失败
*/
public 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 delta 要增加几(大于0)
* @return
*/
public long incr(String key, long delta){
if (delta < 0){
throw new RuntimeException("递增因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, delta);
}
/**
* 递减
* @param key 键
* @param delta 要减少几(大于0)
* @return
*/
public long decr(String key, long delta){
if(delta < 0){
throw new RuntimeException("递减因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, -delta);
}
/*==========Map==========*/
/**
* HashGet
* @param key 键,不能为null
* @param item 项,不能为null
* @return 值
*/
public Object hget(String key, String item){
return redisTemplate.opsForHash().get(key,item);
}
/**
* 获取hashKey对应的所有键值
* @param key 键
* @return 对应的多个键值
*/
public Map<Object, Object> hmget(String key){
return redisTemplate.opsForHash().entries(key);
}
/**
* HashSet
* @param key 键
* @param map 对应多个键值
* @return true成功,false失败
*/
public boolean hmset(String key, Map<String, Object> 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 boolean hmset(String key, Map<String, Object> 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 boolean hset(String key, String item, Object 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 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 void hdel(String key, Object ... item){
redisTemplate.opsForHash().delete(key, item);
}
/**
* 判断hash表中是否有该项的值
* @param key 键 不能为null
* @param item 项 不能为null
* @return true 存在 false不存在
*/
public boolean hHasKey(String key, String item){
return redisTemplate.opsForHash().hasKey(key,item);
}
/**
* hash递增 如果不存在,就会创建一个 并把新增后的值返回
* @param key 键
* @param item 项
* @param by 要增加几(大于0)
* @return
*/
public 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 double hdecr(String key, String item, double by){
return redisTemplate.opsForHash().increment(key, item, -by);
}
/*==========set==========*/
/**
* 根据key获取Set中的所有值
* @param key 键
* @return
*/
public Set<Object> 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 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 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 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 long sGetSetSize(String key){
try {
return redisTemplate.opsForSet().size(key);
}catch (Exception e){
e.printStackTrace();
return 0;
}
}
/**
* 移除值为value的
* @param key 键
* @param values 值 可以是多个
* @return 移除的个数
*/
public long setRemove(String key, Object...values){
try {
Long count = redisTemplate.opsForSet().remove(key, values);
return count;
}catch (Exception e){
e.printStackTrace();
return 0;
}
}
/*==========list==========*/
/**
* 获取list缓存的内容
* @param key 键
* @param start 开始
* @param end 结束 0 到 -1代表所有值
* @return
*/
public List<Object> 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 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 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 值
* @return
*/
public 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 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 值
* @return
*/
public boolean lSet(String key, List<Object> 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 boolean lSet(String key, List<Object> 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 boolean lUpdateIndex(String key, long index, Object value){
try {
redisTemplate.opsForList().set(key, index, value);
return true;
}catch (Exception e){
return false;
}
}
/**
* 移除N个值为value
* @param key 键
* @param count 移除多少个
* @param value 值
* @return 移除的个数
*/
public 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;
}
}
/*==========Zset 有序不重复集合==========*/
/**
* 添加一个元素, zset与set最大的区别就是每个元素都有一个score,因此有个排序的辅助功能; zadd
* @param key
* @param value
* @param score
*/
public boolean ZSet(String key, String value, double score){
try {
redisTemplate.opsForZSet().add(key, value, score);
return true;
}catch (Exception e){
e.printStackTrace();
return false;
}
}
/**
* 删除元素 zrem
* @param key
* @param value
*/
public boolean ZRemove(String key, String value){
try {
redisTemplate.opsForZSet().remove(key, value);
return true;
}catch (Exception e){
e.printStackTrace();
return false;
}
}
/**
* score的增加or减少 zincrby
* @param key
* @param value
* @param score
*/
public Double ZIncrScore(String key, String value, double score){
return redisTemplate.opsForZSet().incrementScore(key, value, score);
}
/**
* 查询value对应的score zscore
* @param key
* @param value
* @return
*/
public Double ZScore(String key, String value){
return redisTemplate.opsForZSet().score(key, value);
}
/**
* 判断value在zset中的排名 zrank
* @param key
* @param value
* @return
*/
public Long ZRank(String key, String value){
return redisTemplate.opsForZSet().rank(key, value);
}
/**
* 查询集合中指定顺序的值, 0 -1 表示获取全部的集合内容 zrange
* 返回有序的集合,score小的在前面
* @param key
* @param start
* @param end
* @return
*/
public Set<String> ZRange(String key, int start, int end){
return redisTemplate.opsForZSet().range(key, start, end);
}
/**
* 查询集合中指定顺序的值和score,0, -1 表示获取全部的集合内容
* @param key
* @param start
* @param end
* @return
*/
public Set<ZSetOperations.TypedTuple<String>> ZRangeWithScore(String key, int start, int end){
return redisTemplate.opsForZSet().rangeWithScores(key, start, end);
}
/**
* 查询集合中指定顺序的值 zrevrange
* 返回有序的集合中,score大的在前面
* @param key
* @param start
* @param end
* @return
*/
public Set<String> ZRevRange(String key, int start, int end){
return redisTemplate.opsForZSet().reverseRange(key, start, end);
}
/**
* 根据score的值,来获取满足条件的集合 zrangebyscore
* @param key
* @param min
* @param max
* @return
*/
public Set<String> ZSortRange(String key, int min, int max){
return redisTemplate.opsForZSet().rangeByScore(key, min, max);
}
}
resources下的application.yml文件
spring:
redis:
host: 127.0.0.1
port: 6379
database: 0
config包下的RedisConfig
package com.work.config;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
public class RedisConfig {
@Bean
@ConditionalOnMissingBean(name = "redisTemplate")
public RedisTemplate<Object, Object> redisTemplate(
RedisConnectionFactory redisConnectionFactory){
RedisTemplate<Object, Object> template = new RedisTemplate<>();
//使用fastjson序列化
Jackson2JsonRedisSerializer fastJsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
// value值的序列化采用fastJsonRedisSerializer
template.setValueSerializer(fastJsonRedisSerializer);
template.setHashValueSerializer(fastJsonRedisSerializer);
// key的序列化采用StringRedisSerializer
template.setKeySerializer(new StringRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean
@ConditionalOnMissingBean(StringRedisTemplate.class)
public StringRedisTemplate stringRedisTemplate(
RedisConnectionFactory redisConnectionFactory) {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}
controller包下的控制器中
/**
* 单点登录部分
*/
/*获取一个cookie*/
String ck = CookieUtils.getCookieValue(req, "p2ptoken");
if (ck != null && ck.length() > 0){
/*指定这个cookie缓存失效的时间300秒*/
redisUtils.expire("p2ptoken:" + ck, 300);
}else {
/*mobile是前端页面中的账号的input框中的id,我已经获取到了前端的mobile,这里不需要*/
/*String name = req.getParameter("mobile");*/
String token = UUID.randomUUID().toString();
/*普通缓存放入并设置时间300秒*/
redisUtils.set("p2ptoken:" + token, mobile, 300);
/*设置Cookie的值 在指定时间内300秒生效*/
CookieUtils.setCookie(req, resp, "p2ptoken", token, 300);
}