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 ...
随机推荐
- 02C++基本语法
基本语法 2.1.1单行注释 // 2.1.2多行注释 /* * */ 2.1.3标识符 C++ 标识符是用来标识变量.函数.类.模块,或任何其他用户自定义项目的名称.一个标识符以字母 A-Z 或 a ...
- 参考整理papers(一)
https://blog.csdn.net/qq_14845119/article/details/82219246 整理了OCR的论文,可以参考一下.还有一些相关论文 论文(poster):Scen ...
- 微服务网关从零搭建——(二)搭建api网关(不带验证)
环境准备 创建空的core2.1 api项目 演示使用名称APIGateWay 过程参考上一篇 完成后在appsettings.json 添加节点 "Setting": { & ...
- Linux kernel-汇编基础
mov ASSEMABLE C LANGUAGE movl %eax,%edx edx = eax; --->register mode movl $0x123,%edx edx = 0x123 ...
- C++ map使用总结
0. Backgroud 此文章源于博主(sunshinewave),转到自己博客以后方便查看 map是STL的一个关联容器,它提供一对一(其中第一个可以称为关键字,每个关键字只能在map中出现一次, ...
- ProgressDialog的样式
ProgressDialog的样式有两种,一种是圆形不明确状态,一种是水平进度条状态 第一种方式:圆形进度条 final ProgressDialog dialog = new ProgressDia ...
- STL中栈stack的用法
头文件: #include <stack> 建立一个栈stack < 类型 > s //例如 stack<int> s 加入一个新的元素s.push( a ) 询问 ...
- 大理石在哪儿(Where is the Marble?,Uva 10474)
现有N个大理石,每个大理石上写了一个非负整数.首先把各数从小到大排序,然后回 答Q个问题.每个问题问是否有一个大理石写着某个整数x,如果是,还要回答哪个大理石上 写着x.排序后的大理石从左到右编号为1 ...
- Radar Installation POJ - 1328 (贪心)
题目大意(vj上的翻译版本) 假定海岸线是无限长的直线.陆地位于海岸线的一侧,海洋位于另一侧.每个小岛是位于海洋中的一个点.对于任何一个雷达的安装 (均位于海岸线上),只能覆盖 d 距离,因此海洋中的 ...
- Python学习-字符串函数操作3
字符串函数操作 isprintable():判断一个字符串中所有字符是否都是可打印字符的. 与isspace()函数很相似 如果字符串中的所有字符都是可打印的字符或字符串为空返回 True,否则返回 ...