java无状态登录实现方式之ThreadLocal+Cookie
注:本文提到的无状态指的是无需session完毕认证、取用户封装信息。
无状态的优点:
1。多应用单点登录:在多应用的时候仅仅需在登录server登录后。各子应用无需再次登录。
2。多server集群:无需制作会话共享的缓存就可以实现。
此方案的缺点:
1,依赖于cookie,尽管如今主流浏览器都支持cookie。
2。单点登录须要各子应用属于同一主域名下(跨主域名无法实现)。
实现原理:
登录时封装用户信息,并将用户信息通过序列化加密写到用户cookie。当用户下次请求应用server时,过滤器将用户信息取到反解密反序列化后放入ThreadLocal中,利用ThreadLocal的线程安全特性,之后操作取到用户信息。
用户封装信息类
package com.xxx.commons.framework.bean;
import java.io.Serializable;
public class Principal implements Serializable {
private static final long serialVersionUID = -1373760761780840081L;
private Long id;
private String username;
private Integer userType;
private Long pharmacyId;
private Long saleManId;
private Long ydId;
private String name;
public Principal(Long id, String username,Integer userType,Long pharmacyId,Long saleManId,Long ydId,String name) {
this.id = id;
this.username = username;
this.userType = userType;
this.pharmacyId = pharmacyId;
this.saleManId = saleManId;
this.ydId = ydId;
this.setName(name);
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
@Override
public String toString() {
return username;
}
public Integer getUserType() {
return userType;
}
public void setUserType(Integer userType) {
this.userType = userType;
}
/**
* @return pharmacyId
*
*/
public Long getPharmacyId() {
return pharmacyId;
}
/**
* @param pharmacyId
*
*/
public void setPharmacyId(Long pharmacyId) {
this.pharmacyId = pharmacyId;
}
/**
* @return saleManId
*
*/
public Long getSaleManId() {
return saleManId;
}
/**
* @param saleManId
*
*/
public void setSaleManId(Long saleManId) {
this.saleManId = saleManId;
}
/**
* @return ydId
*
*/
public Long getYdId() {
return ydId;
}
/**
* @param ydId
*
*/
public void setYdId(Long ydId) {
this.ydId = ydId;
}
/**
* get name
* @return the name
*
*/
public String getName() {
return name;
}
/**
* set name
* @param name
*
*/
public void setName(String name) {
this.name = name;
}
}
用户信息工具类
/**
* Copyright RH Corporation 2014 版权全部
* Created 2014年12月18日 下午1:24:27
* @version V1.0
*/
package com.xxx.commons.framework.utils; import com.xxx.commons.framework.bean.Principal; /**
* 加入类描写叙述
* @author ElongDeo
* @version 1.0
* Created 2014年12月18日 下午1:24:27
*/
public class UserUtil {
public static final ThreadLocal<Principal> principal = new ThreadLocal<Principal>(); public static Principal getUserPrincipal(){
Principal principal = UserUtil.principal.get();
return principal;
} public static String getUserName(){
String userName = "";
Principal principal = getUserPrincipal();
if(principal!=null){
userName = principal.getUsername();
}
return userName;
} public static String getName(){
String name = "";
Principal principal = getUserPrincipal();
if(principal!=null){
name = principal.getName();
}
return name;
} public static Long getUserId(){
Long userId = null;
Principal principal = getUserPrincipal();
if(principal!=null){
userId = principal.getId();
}
return userId;
} public static Integer getUserType(){
Integer userType = null;
Principal principal = getUserPrincipal();
if(principal!=null){
userType = principal.getUserType();
}
return userType;
} public static Long getPharmacyId(){
Long pharmacyId = null;
Principal principal = getUserPrincipal();
if(principal!=null){
pharmacyId = principal.getPharmacyId();
}
return pharmacyId;
} public static Long getSaleManId(){
Long saleManId = null;
Principal principal = getUserPrincipal();
if(principal!=null){
saleManId = principal.getSaleManId();
}
return saleManId;
} public static Long getYdId(){
Long ydId = null;
Principal principal = getUserPrincipal();
if(principal!=null){
ydId = principal.getYdId();
}
return ydId;
} public static Long getBuyerId(){
Long buyerId = null;
Integer userType = getUserType();
if(userType != null && userType > Constants.USER_ADMIN_TYPE){
if(userType.equals(Constants.USER_PHARMARY_TYPE)){
buyerId = getPharmacyId();
}else{
buyerId = getYdId();
}
}
return buyerId;
} public static String getCartYn(){
String cartYn = "no";
Integer userType = getUserType();
if(userType > Constants.USER_ADMIN_TYPE){
cartYn = "yes";
}
return cartYn;
} }
cookies工具类(用来封装/解析用户信息)
/**
* CookieUtils.java Copyright © 2008-2013 lefeng.com Inc. All Rights Reserved.
*/
package com.xxx.commons.framework.utils; import java.util.HashMap;
import java.util.Map; import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import org.apache.commons.codec.binary.Base64; import com.xxx.commons.framework.bean.Principal;
import com.xxx.commons.items.PropertiesFileLoader; /**
* <pre>
* <P>Author : ElongDeo</P>
* <P>Date : 2014-3-10 </P>
* <P>Cookie操作辅助类</P>
* </pre>
*/
public class CookieUtils {
public static String DOMAIN = ".xxx.com";
public static final String COOKIE_TOKEN_LOGIN = "xxx_token";
public static final String COOKIE_USER_INFO = "xxx_user"; static {
PropertiesFileLoader instance = PropertiesFileLoader.getInstance();
DOMAIN = instance.getProerties("config/user.properties","domain");
}
/**
* 设置cookie
* @param response
* @param name cookie名字
* @param value cookie值
* @param maxAge cookie生命周期 以秒为单位
*/
public static void addCookie(HttpServletResponse response,String name,String value,int maxAge, String domain){
Cookie cookie = new Cookie(name,value);
cookie.setDomain(domain);
cookie.setPath("/");
if(maxAge>0) cookie.setMaxAge(maxAge);
response.addCookie(cookie);
} /**
* 依据名字获取cookie
* @param request
* @param name cookie名字
* @return
*/
public static Cookie getCookieByName(HttpServletRequest request,String name){
Map<String,Cookie> cookieMap = readCookieMap(request);
if(cookieMap.containsKey(name)){
Cookie cookie = (Cookie)cookieMap.get(name);
return cookie;
}else{
return null;
}
} /**
* 将cookie封装到Map里面
* @param request
* @return
*/
public static Map<String,Cookie> readCookieMap(HttpServletRequest request){
Map<String,Cookie> cookieMap = new HashMap<String,Cookie>();
Cookie[] cookies = request.getCookies();
if(null!=cookies){
for(Cookie cookie : cookies){
cookieMap.put(cookie.getName(), cookie);
}
}
return cookieMap;
} public static Principal getPrincipal(HttpServletRequest request) {
Cookie cookie = getCookieByName(request, COOKIE_USER_INFO);
if (cookie != null && !"".equals(cookie.getValue())) {
try {
return (Principal) SerializeUtils.deserialize(Base64.decodeBase64(cookie.getValue()));
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
} public static void setPrincipal(HttpServletResponse response, Principal principal) {
try {
addCookie(response, COOKIE_USER_INFO, Base64.encodeBase64String(SerializeUtils.serialize(principal)), 0, DOMAIN);
} catch (Exception e) {
e.printStackTrace();
}
} public static void removePrincipal(HttpServletResponse response) {
try {
addCookie(response, COOKIE_USER_INFO, null, 0, DOMAIN);
} catch (Exception e) {
e.printStackTrace();
}
} }
登录写入cookie代码片段
Principal principal = new Principal(userId, login, userType, pharmacyId, saleManId, ydId, name);
//假设正确,那么写cookie而且正确跳转
try { CookieUtils.setPrincipal(response, principal);
redirect = StringUtils.isEmpty(request.getParameter("redirect"))? LOGIN_REDIRECT_URL:request.getParameter("redirect");// 须要处理首页
PrintUtils.printToMobile(response, new ResultObject<Object>(1, redirect), "json");
return;
} catch (Exception e) {
e.printStackTrace();
}
过滤器获取用户信息并放入ThreadLocal
/**
*
*/
package com.xxx.commons.framework.filters; import java.io.IOException;
import java.util.HashSet;
import java.util.Set; import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import org.apache.log4j.Logger; import com.xxx.commons.framework.utils.CookieUtils;
import com.xxx.commons.framework.utils.StringUtils;
import com.xxx.commons.framework.utils.UserUtil; /**
* Servlet Filter implementation class AuthenticationFilter
*/
public class PrincipalFilter implements Filter { Logger logger = Logger.getLogger(PrincipalFilter.class);
private static String notLoginUrl = null; // 被忽略的全部URL.
private static Set<String> mobjIgnoredUrls = new HashSet<String>(); /**
* @see Filter#doFilter(ServletRequest, ServletResponse, FilterChain)
*/
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
UserUtil.principal.set(CookieUtils.getPrincipal((HttpServletRequest)request));
if(notLoginUrl != null && UserUtil.principal.get() == null && !isIgnoreUrl((HttpServletRequest)request)){
((HttpServletResponse)response).sendRedirect(notLoginUrl);
return;
}
chain.doFilter(request, response);
} /**
* 加入描写叙述
* @author ElongDeo 2015年6月26日
* @param filterConfig
* @throws ServletException
*/
@Override
public void init(FilterConfig filterConfig) throws ServletException {
notLoginUrl = filterConfig.getInitParameter("notLoginUrl");
// 包装要被忽略的URL
String urlText = filterConfig.getInitParameter("ignoredUrls");
if(urlText != null){
urlText = urlText.replaceAll("\r\n", "").replaceAll("\t", "").trim();
String[] urls = urlText.split(",");
for (int i = 0; i < urls.length; i++) {
mobjIgnoredUrls.add(urls[i]);
}
}
}
/**
* <pre>
* 验证是否要被忽略的URL.
* </pre>
*
* @param pobjRequest
* the pobjRequest
* @return true, if is ignore url
* @author guotianchi 2011-4-20
*/
private boolean isIgnoreUrl(HttpServletRequest pobjRequest) {
String objRequestUri = pobjRequest.getRequestURI();
if (StringUtils.isNotEmpty(objRequestUri)) {
int index = objRequestUri.lastIndexOf('/');
if (index >= 0
&& index < (objRequestUri.length() - 1)
&& mobjIgnoredUrls.contains(objRequestUri.substring(
index + 1, objRequestUri.length()))) {
return true;
}
}
return false;
} /**
* 加入描写叙述
* @author ElongDeo 2015年6月26日
*/
@Override
public void destroy() {
}
}
应用serverweb.xml配置
<filter>
<filter-name>PrincipalFilter</filter-name>
<filter-class>com.xxx.commons.framework.filters.PrincipalFilter</filter-class>
<init-param>
<param-name>notLoginUrl</param-name>
<param-value>/common/logout.htm</param-value>
</init-param>
<init-param>
<param-name>ignoredUrls</param-name>
<param-value>logout.htm</param-value>
</init-param>
</filter> <filter-mapping>
<filter-name>PrincipalFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

java无状态登录实现方式之ThreadLocal+Cookie的更多相关文章
- JWT+Interceptor实现无状态登录和鉴权
无状态登录原理 先提一下啥是有状态登录 单台tomcat的情况下:编码的流程如下 前端提交表单里用户的输入的账号密码 后台接受,查数据库, 在数据库中找到用户的信息后,把用户的信息存放到session ...
- java 无状态和有状态区别
诸位Java程序员,想必大家对SimpleDateFormat并不陌生.不过,你是否知道,SimpleDateFormat不是线程安全的(thread safe).这意味着,下面的代码是错误的: ...
- HTTP要点概述:五,HTTP的无状态性,持久连接,Cookie
一,HTTP的无状态性: HTTP 是一种不保存状态,无状态(stateless)协议.HTTP 协议自身不对请求和响应之间的通信状态进行保存.也就是说在 HTTP 这个级别,协议对于发送过的请求或响 ...
- Flask基础(10)-->http的无状态协议解决办法一(客户端cookie)
http的无状态协议 http是一种无状态协议,浏览器请求服务器时无状态的 什么是无状态? 无状态:指的是一次用户请求时,浏览器.服务器无法知道之前这个用户做过什么,每次请求都是一次新的请求. 无状态 ...
- Java Web学习(五)session、cookie、token
文章更新时间:2020/09/14 一.引言 动态网页兴起后,会话管理变成开发者需要考虑的一个问题,由于HTTP请求是无状态的,为了区分每个用户,此时引入了会话标识(sessionId)的概念,但是存 ...
- Java 实现 SSH 协议的客户端登录认证方式--转载
背景 在开篇之前,让我们先对 SSH 协议有个宏观的大致了解,这样更有利于我们对本文的加深了解.首先要提到的就是计算机网络协议,所谓计算机网络协议,简单的说就是定义了一套标准和规则,使得不同计算机之间 ...
- paip.提升性能----java 无锁结构(CAS, Atomic, Threadlocal, volatile, 函数式编码, 不变对象)
paip.提升性能----java 无锁结构(CAS, Atomic, Threadlocal, volatile, 函数式编码, 不变对象) 1 锁的缺点 2 CAS(Compare ...
- 「小程序JAVA实战」java-sesion的状态会话与无状态会话(38)
转自:https://idig8.com/2018/09/02/xiaochengxujavashizhanjava-sesiondezhuangtaihuihuayuwuzhuangtaihuihu ...
- Java 有状态和无状态对象的区别
无状态会话Bean 无状态就是对于一次操作,不能保存数据.无状态对象(Stateless Bean)是没有实例变量的对象,不能保存数据,是不变类,是线程安全的.例如: public class ...
随机推荐
- handlesocket.md
[介绍](http://www.uml.org.cn/sjjm/201211093.asp ) * 查看启动参数 `service mariadb status > st.txt` ...
- Learning Face Age Progression: A Pyramid Architecture of GANs
前言 作为IP模式识别的CNN初始模型是作为单纯判别式-模式识别存在的,并以此为基本模型扩展到各个方向.基本功能为图像判别模型,此后基于Loc+CNN的检测模型-分离式.end2end.以及MaskC ...
- 如何安装Ant,配置环境变量??
Apache Ant,是一个将软件编译.测试.部署等步骤联系在一起加以自动化的一个工具,大多用于Java环境中的软件开发. Ant是一个基于Java,并且主要用于Java工程的构建工具.Ant本意是A ...
- Flask框架 之路由
一.视图函数路由规则 from flask import Flask, redirect, url_for # 创建flask应用对象 # __name__ 代表当前模块名称 # flask以当前目录 ...
- Django - 自定义simple_tag
使用现有函数: 通过对传入的参数,后面跟一个管道符号+python函数,来完成对传入参数的修改. 返回值 自定义simple_tag: 具体操作步骤如下: 1.在某个app下,创建目录template ...
- php第三十节课
文件操作 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3 ...
- python_ 学习笔记(基本数据类型)
python3有6中标准数据类型:Number(数字).String(字符串).List(列表).Tuple(元组).Dictionary(字典).Set(集合)不可变数据:Number.String ...
- log 框架 之间的关系
日志框架分为两大部分 一部分是日志框架的抽象层,一部分是日志框架的具体实现 slf4j: 日志框架的抽象层 log4j,logback 日志框架的具体实现 如上图所示: slf4j的具体实现是:slf ...
- 判断项目中是否有slf4j的实现类
/** * 判断项目中是否有slf4j的实现类 */ @org.junit.Test public void test() { try { Enumeration<URL> resourc ...
- 【hdu 1043】Eight
[题目链接]:http://acm.hdu.edu.cn/showproblem.php?pid=1043 [题意] 会给你很多组数据; 让你输出这组数据到目标状态的具体步骤; [题解] 从12345 ...