登录使用shiro框架的SecurityUtil.getSubject().login(new UserNamePasswordToken("telphone","pass“))
退出:SecurityUtil.getSubject().loginOut();
1、单点登录适用于多应用关联的情况(如果是多节点单点登录,那么需要使用shiro框架的session,来保证对节点获取的sessionId是相同的,见另一篇shiro框架文章https://blog.csdn.net/wohaqiyi/article/details/81342741)
2、需要创建拦截器来识别单点登录信息
3、登录Controller,需要将token放到COOKIE里面,设置域为“/”,这样同服务器下的应用可获得添加的该token实现单点登录
package com.liu.web.sso;
import com.alibaba.fastjson.JSON;
import com.liu.beans.OutputObject;
import com.liu.web.Filter.FilterController;
import com.liu.web.utils.AesEncryDecryUtil;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import redis.clients.jedis.JedisCluster;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
@RestController
@RequestMapping(path = "/SingleSign")
public class SingleSignController {
@Autowired
private JedisCluster jedisCluster;
private String SESSION_USER="SESSION_USER_";
//sesssion失效 60秒
private int SESSION_USER_EXPIRE=60;
private String TOKEN_INFO="TOKEN_INFO_";
Logger logger = LoggerFactory.getLogger(SingleSignController.class);
@RequestMapping(value = "/login",method = RequestMethod.POST)
public OutputObject login(String account, String password, HttpSession session, HttpServletResponse response){
OutputObject out = new com.liu.beans.OutputObject();
try{
//
String sessionId = session.getId();
//每次重启工程就不一样,只要不重启工程,每次请求的值都是相同的,所以最好还是用sessionId来判断每个IP
logger.info("--------sessionId:"+sessionId);
if(StringUtils.isNotBlank(account) && "123".equals(password)){
Map<String,Object> userMap = new HashMap();
userMap.put("account",account);
userMap.put("password",password);
logger.info("---------userMap:"+ JSON.toJSONString(userMap));
//验证成功
//根据sessionId将用户信息保存到redis
jedisCluster.setex(SESSION_USER+sessionId,SESSION_USER_EXPIRE,JSON.toJSONString(userMap));
//添加cookie
//生成token,需将token加密放到cookie
String token = UUID.randomUUID().toString().replace("-","")+"_"+account;
logger.info("------token:"+token);
String key = AesEncryDecryUtil.getAesKey();
String encryToken = AesEncryDecryUtil.aesEncry(token,key).toString();
logger.info("------encryToken:"+encryToken);
//将token根据员工账号保存到redis
jedisCluster.setex(TOKEN_INFO+account,SESSION_USER_EXPIRE,JSON.toJSONString(userMap));
//添加cookie到response
Cookie cookie = new Cookie("my-Token",encryToken);
//setPath("/")设置同一服务器都可获得改cookie,(适用于多个应用使用该cookie)
cookie.setPath("/");
//cookie的有效时长
cookie.setMaxAge(6000);
// 通过js脚本将无法读取到cookie信息,这样能有效的防止XSS攻击,窃取cookie内容,这样就增加了cookie的安全性
cookie.setHttpOnly(true);
response.addCookie(cookie);
//将用户信息根据token保存到redis
}
// String requestSessionId = request.getRequestedSessionId();
// //第一此登录时requestSessionId为NULL,之后和sessionId值一样
// logger.info("-------requestSessionId:"+requestSessionId);
// StringBuffer url = request.getRequestURL();
// String addr = request.getRemoteAddr();
// String host =request.getRemoteHost();
// int port = request.getRemotePort();
// logger.info("-------url:"+url+"-------addr:"+addr+"------host:"+host+"------port:"+port);
}catch (Exception e){
}
return out;
}
}
4、增加拦截器配置
package com.liu.web.sso;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
@Configuration
public class InterceptorConfig extends WebMvcConfigurerAdapter {
@Bean
public SingleSignOnAdapter getSingleSignOnAdapter(){
return new SingleSignOnAdapter();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
//增加单点登录拦截器,除了error,swagger-sources页面,其余路径全部拦截
registry.addInterceptor(getSingleSignOnAdapter()).addPathPatterns("/**")
.excludePathPatterns("/error")
.excludePathPatterns("/swagger-sources");
}
}
5、单点登录拦截器实现类
package com.liu.web.sso;
import com.alibaba.dubbo.rpc.service.GenericException;
import com.liu.web.Filter.FilterController;
import com.liu.web.utils.JsonUtils;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import redis.clients.jedis.JedisCluster;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;
public class SingleSignOnAdapter extends HandlerInterceptorAdapter {
Logger logger = LoggerFactory.getLogger(FilterController.class);
@Autowired
private JedisCluster jedisCluster;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
try{
String sessionId = request.getSession().getId();
Cookie[] cookies = request.getCookies();
for(Cookie cookie:cookies){
if("my-token".equals(cookie.getName())){
//为拼接每个用户账号的token
String token = cookie.getValue();
String account = token.split("_")[1];
String redisKey = "ACCOUNT_INFO_"+account;
String userInfo = jedisCluster.get(redisKey);
if(StringUtils.isBlank(userInfo)){
//为空则根据token远程调用登陆工程的服务,返回当前用的信息
String res = "{\"rstCode\":\"0\",\"bean\":{\"account\":\"001\"}}";
Map<String,String> map = BeanUtils.describe(res);
String rstCode = map.get("rstCode");
if("0".equals(rstCode)) {
String userString = map.get("bean");
if(StringUtils.isBlank(userString)){
throw new GeneralException("-1001","登录失效!");
}
jedisCluster.setex(redisKey,300,userString);
return true;
}else{
logger.info("------服务繁忙稍后再试");
throw new GeneralException("2999","服务繁忙稍后再试!");
}
}else{
return true;
}
}
}
logger.info("----------------登录失效");
throw new GeneralException("-1001","登录失效!");
}catch (Exception e){
logger.info("----------------登录失效");
throw new GeneralException("-1001","登录失效!");
}
}
}