经过前两章,我们已经将ssm与shiro配置完毕,现在添加redis的工具类
@Component
public class RedisUtil {
private RedisTemplate<Serializable, Object> redisTemplate;
/**
* 批量删除对应的value
*
* @param keys
*/
public void remove(final String... keys) {
for (String key : keys) {
remove(key);
}
}
/**
* 批量删除key
*
* @param pattern
*/
public void removePattern(final String pattern) {
Set<Serializable> keys = redisTemplate.keys(pattern);
if (keys.size() > 0)
redisTemplate.delete(keys);
}
/**
* 删除对应的value
*
* @param key
*/
public void remove(final String key) {
if (exists(key)) {
redisTemplate.delete(key);
}
}
/**
* 判断缓存中是否有对应的value
*
* @param key
* @return
*/
public boolean exists(final String key) {
return redisTemplate.hasKey(key);
}
/**
* 读取缓存
*
* @param key
* @return
*/
public Object get(final String key) {
Object result = null;
ValueOperations<Serializable, Object> operations = redisTemplate
.opsForValue();
result = operations.get(key);
return result;
}
/**
* 写入缓存
*
* @param key
* @param value
* @return
*/
public boolean set(final String key, Object value) {
boolean result = false;
try {
ValueOperations<Serializable, Object> operations = redisTemplate
.opsForValue();
operations.set(key, value);
result = true;
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
/**
* 写入缓存
*
* @param key
* @param value
* @return
*/
public boolean set(final String key, Object value, Long expireTime) {
boolean result = false;
try {
ValueOperations<Serializable, Object> operations = redisTemplate
.opsForValue();
operations.set(key, value);
redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
result = true;
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
public void setRedisTemplate(
RedisTemplate<Serializable, Object> redisTemplate) {
this.redisTemplate = redisTemplate;
}
}
为了让redis帮我们管理session 需要在登陆成功之后将session写入redis中,我们修改一下登陆的方法
//登录
@RequestMapping(value = "/login",method = RequestMethod.POST)
public R webLogin(@RequestBody Map<String, String> parameters){
Map<String,Object> map = new HashMap<>();
String newPassword = PasswordUtil.encodePwd(parameters.get("password"));
UsernamePasswordToken token = new UsernamePasswordToken(parameters.get("userName"),newPassword);
Subject subject = SecurityUtils.getSubject();
try{
subject.login(token);
String loginToken = validateSucceed(null, new SsoUser(parameters.get("userName"), newPassword), false);
map.put("token",loginToken);
return R.ok(map);
}catch (Exception e){
e.printStackTrace();
return R.error(R.CODE_LOGIN_ERROR,"用户名或者密码错误");
}
}
private String validateSucceed(String backUrl, SsoUser ssoUser, boolean rememberMe) throws Exception {
// 生成vt
String token = UUID.randomUUID().toString().replace("-", "").toLowerCase();
// 添加vtcookie
CookieUtil.setCookie(httpServletRequest, httpServletResponse, "vt", token, true);
// 添加vt用户到redis
//redisService.set(vt, MAPPER.writeValueAsString(ssoUser), loginConfig.getVtRedisMaxTime());
String userString = JSON.toJSONString(ssoUser);
redisUtil.set(token,userString, 1800l); // 30分钟的有效时间 通过redis来模拟管理session信息
logger.info("new token={}", token);
return token;
}
上面的代码比较容易理解,就是先生成一个token ,存入cookie的同时 存入redis然后返回前台
最后一步,要修改shiro的过滤器,在登陆验证的时候不走shiro的session而是走redis
public class ClientAuthenticationFilter extends OncePerRequestFilter {
private static final Logger LOGGER = LoggerFactory.getLogger(ClientAuthenticationFilter.class);
private RedisUtil redisUtil;
@Override
protected void doFilterInternal(ServletRequest request, ServletResponse response
, FilterChain filterChain) throws ServletException, IOException {
redisUtil = (RedisUtil)SpringContextUtil.getBean("redisUtil");
// 1、 从cookie中获取 2、从header中获取token 3、从参数中获取token
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
UserThreadLocal.clear();
String vt = null;
LOGGER.info("ClientAuthenticationFilter doFilterInternal, url : {}", httpServletRequest.getRequestURL());
// 开始登录状态验证
vt = CookieUtil.getCookieValue(httpServletRequest, "vt", true);
LOGGER.info("ClientAuthenticationFilter doFilterInternal, vt : {}", vt);
if(vt == null) { // 如果cookie里面没有 就从请求头中获取token
vt = httpServletRequest.getHeader("token");
}
if (StringUtils.isNotEmpty(vt)) {
/* cookie有效,接下来判断是否超时 */
String user = (String) redisUtil.get(vt);
// 如果不为空 则表示没有超时
if (StringUtils.isNotBlank(user)) {
SsoUser ssoUser = JSON.parseObject(user, SsoUser.class);
// 将用户重新设置时间
redisUtil.set(vt,user, 1800l);
LOGGER.info("logincheck success !");
// shiro验证权限
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(ssoUser.getAccount(), ssoUser.getPassword());
subject.login(token);
filterChain.doFilter(httpServletRequest, httpServletResponse);
}else{
// session 超时需要重新登陆
Map<String, Object> resultMap = new HashMap<>();
resultMap.put("status", false);
resultMap.put("code", R.CODE_LOGIN_TIMEOUT);
resultMap.put("timeout", "timeout");
resultMap.put("msg" , "timeout");
String resultString = JSON.toJSONString(resultMap);
httpServletResponse.setContentType("application/json; charset=utf-8");
httpServletResponse.setHeader("Access-Control-Allow-Origin","*");
PrintWriter writer = httpServletResponse.getWriter();
writer.print(resultString);
writer.flush();
writer.close();
}
} else { // 没有登陆
Map<String, Object> resultMap = new HashMap<>(); resultMap.put("status", false); resultMap.put("code", R.CODE_LOGIN_NO); resultMap.put("timeout", "no"); resultMap.put("msg" , "not login"); String resultString = JSON.toJSONString(resultMap); httpServletResponse.setContentType("application/json; charset=utf-8"); httpServletResponse.setHeader("Access-Control-Allow-Origin","*"); PrintWriter writer = httpServletResponse.getWriter(); writer.print(resultString); writer.flush(); writer.close();
}
}
}
修改shiro 的配置文件 加入红色的这行
HashMap<String, Filter> myFilters = new HashMap<>();
myFilters.put("authc", new ClientAuthenticationFilter());
shiroFilterFactoryBean.setFilters(myFilters);
其他不用修改 这样就完成了分布式session的交由redis管理的工作
CookieUtil 如下
public final class CookieUtil {
protected static final Logger LOGGER = LoggerFactory.getLogger(CookieUtil.class);
/**
* <得到Cookie的值,不解码>
*
* @param request
* @param cookieName
* @return
* @throws UnsupportedEncodingException
*/
public static String getCookieValue(HttpServletRequest request, String cookieName)
throws UnsupportedEncodingException {
return getCookieValue(request, cookieName, false);
}
/**
* <得到Cookie的值,并以UTF-8的方式解码>
*
* @param request
* @param cookieName
* @param isDecoder
* @return
* @throws UnsupportedEncodingException
*/
public static String getCookieValue(HttpServletRequest request, String cookieName, boolean isDecode)
throws UnsupportedEncodingException {
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)) {
if (isDecode) {
retValue = URLDecoder.decode(cookieList[i].getValue(), "UTF-8");
} else {
retValue = cookieList[i].getValue();
}
break;
}
}
} catch (UnsupportedEncodingException e) {
LOGGER.error("Cookie Decode Error.", e);
throw new UnsupportedEncodingException(ExceptionUtil.getTrace(e));
}
return retValue;
}
/**
* <得到Cookie的值,以自定义的方式解码>
*
* @param request
* @param cookieName
* @param encodeString
* @return
* @throws UnsupportedEncodingException
*/
public static String getCookieValue(HttpServletRequest request, String cookieName, String encodeString)
throws UnsupportedEncodingException {
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) {
LOGGER.error("Cookie Decode Error.", e);
throw new UnsupportedEncodingException(ExceptionUtil.getTrace(e));
}
return retValue;
}
/**
* <设置Cookie的值,不设置生效时间默认浏览器关闭即失效,也不编码>
*
* @param request
* @param response
* @param cookieName
* @param cookieValue
* @throws Exception
*/
public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
String cookieValue) throws Exception {
setCookie(request, response, cookieName, cookieValue, -1);
}
/**
* <设置Cookie的值,在指定时间内生效,但不编码>
*
* @param request
* @param response
* @param cookieName
* @param cookieValue
* @param cookieMaxage
* @throws Exception
*/
public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
String cookieValue, int cookieMaxage) throws Exception {
setCookie(request, response, cookieName, cookieValue, cookieMaxage, false);
}
/**
* <设置Cookie的值,不设置生效时间,UTF-8编码>
*
* @param request
* @param response
* @param cookieName
* @param cookieValue
* @param isEncode
* @throws Exception
*/
public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
String cookieValue, boolean isEncode) throws Exception {
setCookie(request, response, cookieName, cookieValue, -1, isEncode);
}
/**
* <设置Cookie的值,在指定时间内生效,UTF-8编码>
*
* @param request
* @param response
* @param cookieName
* @param cookieValue
* @param cookieMaxage
* @param isEncode
* @throws Exception
*/
public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
String cookieValue, int cookieMaxage, boolean isEncode) throws Exception {
doSetCookie(request, response, cookieName, cookieValue, cookieMaxage, isEncode);
}
/**
* <设置Cookie的值,在指定时间内生效,自定义编码参数>
*
* @param request
* @param response
* @param cookieName
* @param cookieValue
* @param cookieMaxage
* @param encodeString
* @throws Exception
*/
public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
String cookieValue, int cookieMaxage, String encodeString) throws Exception {
doSetCookie(request, response, cookieName, cookieValue, cookieMaxage, encodeString);
}
/**
* <删除Cookie带cookie域名>
*
* @param request
* @param response
* @param cookieName
* @throws Exception
*/
public static void deleteCookie(HttpServletRequest request, HttpServletResponse response, String cookieName)
throws Exception {
doSetCookie(request, response, cookieName, "", 0, false);
}
/**
* <设置Cookie的值,并使其在指定时间内生效,UTF-8编码>
*
* @param request
* @param response
* @param cookieName
* @param cookieValue
* @param cookieMaxage
* @param isEncode
* @throws Exception
*/
private static final void doSetCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
String cookieValue, int cookieMaxage, boolean isEncode) throws Exception {
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
cookie.setDomain(getDomainName(request));
cookie.setPath("/");
response.addCookie(cookie);
} catch (Exception e) {
LOGGER.error("Cookie Encode Error.", e);
throw new Exception(ExceptionUtil.getTrace(e));
}
}
/**
* <设置Cookie的值,并使其在指定时间内生效,自定义编码>
*
* @param request
* @param response
* @param cookieName
* @param cookieValue
* @param cookieMaxage
* @param encodeString
* @throws Exception
*/
private static final void doSetCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
String cookieValue, int cookieMaxage, String encodeString) throws Exception {
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
cookie.setDomain(getDomainName(request));
cookie.setPath("/");
response.addCookie(cookie);
} catch (Exception e) {
LOGGER.error("Cookie Encode Error.", e);
throw new Exception(ExceptionUtil.getTrace(e));
}
}
/**
* <得到cookie的域>
*
* @param request
* @return
* @throws URISyntaxException
*/
private static final String getDomainName(HttpServletRequest request) throws URISyntaxException {
String domainName = null;
String serverName = request.getRequestURL().toString();
String serverName1 = request.getServerName();
int serverPort = request.getServerPort();
if (serverName == null || serverName.equals("")) {
domainName = "";
} else {
URI uri = new URI(serverName);
String host = uri.getHost();
if (Pattern.compile("(?i)[a-z]").matcher(host).find()) {
// 如果是包含字母,是域名
final String[] domains = host.split("\\.");
int len = domains.length;
if (len > 3) {
domainName = domains[len - 3] + "." + domains[len - 2] + "." + domains[len - 1];
} else if (len <= 3 && len > 1) {
domainName = domains[len - 2] + "." + domains[len - 1];
} else {
domainName = host;
}
} else {
domainName = host;
}
}
LOGGER.info("domainName:{}", domainName);
return domainName;
}
}