零碎01-登录验证
目录
一、背景故事
二、解决方案分析
1. 基于Cookie的认证
2. 基于Session的认证
3. 基于JWT的认证
三、基于JWT的Filter和Intercept实现示例
四、总结
一、背景故事
酷乐是一名技术栈不详,遇强则强的程序员。有一天,公司开发了一个内部管理系统,初期为了便于开发测试,系统的某些接口并未加上严格的访问控制。测试人员在浏览器地址栏中直接输入系统某模块的URL,发现竟然可以不登录直接访问内容。这一漏洞很快被发现,然而其背后的登录认证校验机制的问题引发了团队的关注和重视。这一问题不仅影响系统安全,更带来潜在的数据泄露风险。
于是,酷乐开始思考如何设计一个有效的登录认证机制,以确保未经授权的访问可以被拦截。
二、解决方案分析
在登录认证校验中,主流方案包括基于Cookie、Session和JWT(JSON Web Token)三种方式。每种方案都有各自的特点与适用场景。
1. 基于Cookie的认证
-
原理:当用户登录成功后,服务器会生成一个Cookie并存储在客户端浏览器中。后续每次请求时,浏览器会自动将该Cookie发送至服务器,服务器根据该Cookie验证用户的身份。
-
使用方式:通常在服务器端配置Cookie的生成、加密以及过期时间,前端则无需手动处理,浏览器会自动附带Cookie。
-
优缺点
-
优点:实现简单,不需要客户端过多参与,适合小型系统。
-
缺点:安全性较低,Cookie容易被窃取,且在分布式系统中,服务器状态难以共享。
-
2. 基于Session的认证
-
原理:Session是一种将用户状态信息存储在服务器上的方式。用户登录后,服务器创建一个Session,并将Session ID通过Cookie返回给客户端。后续请求携带该Session ID,服务器可以根据此Session ID查找用户信息。
-
使用方式:后端需要有一套Session管理机制,将Session ID与用户信息进行映射。一般来说,Session可存储在数据库或缓存中。
-
优缺点
-
优点:服务器可完全控制用户状态,安全性比Cookie更高。
-
缺点:对服务器存储和管理有较大开销,尤其在高并发场景下,对Session同步有较高要求。
-
3. 基于JWT的认证
-
原理:JWT(Json Web Token)是一种将用户信息以JSON格式加密后生成的Token。登录后,服务器生成JWT并返回给客户端,客户端在后续请求时将JWT放在HTTP头部发送给服务器,服务器通过解析JWT验证用户身份。
-
使用方式:登录成功后生成JWT,放在HTTP Header的
Authorization
字段中。JWT包含有效期,过期后需要重新登录获取。 -
优缺点
-
优点:无状态机制,不依赖服务器存储,便于跨域和分布式环境。
-
缺点:Token一旦泄露,存在被恶意使用的风险,且JWT不支持注销,难以实时失效控制。
-
三、基于JWT的Filter和Intercept实现示例
在基于JWT认证的应用中,常用的方式是通过Filter或Intercept机制来拦截请求,判断JWT的合法性与有效性。
使用到的
示例代码:
-
Filter实现
// JWT Filter 实现示例 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 java.io.IOException; public class JwtAuthenticationFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { // 初始化过滤器 } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest httpRequest = (HttpServletRequest) request; String jwtToken = httpRequest.getHeader("Authorization"); if (jwtToken == null || !isValidToken(jwtToken)) { response.getWriter().write("Unauthorized"); return; } // 继续执行请求 chain.doFilter(request, response); } private boolean isValidToken(String token) { // 验证JWT的有效性,例如检查签名和有效期 // 这里需要现将json转为object return true; // 此处为示例,实际需要替换为真正的验证逻辑 } @Override public void destroy() { // 销毁过滤器 } }
该过滤器会在每次请求时检查是否携带有效的JWT,无效或过期的Token将会被拦截,返回“Unauthorized”提示信息。
-
Intercept实现
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @Component public class JwtInterceptor implements HandlerInterceptor { @Autowired private UserDetailsService userDetailsService; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String token = request.getHeader("Authorization"); if (token != null && token.startsWith("Bearer ")) { token = token.substring(7); // 去掉"Bearer "前缀 try { String username = JwtUtil.getUsernameFromToken(token); if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { UserDetails userDetails = userDetailsService.loadUserByUsername(username); if (JwtUtil.validateToken(token, userDetails)) { UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken( userDetails, null, userDetails.getAuthorities()); SecurityContextHolder.getContext().setAuthentication(authentication); return true; } } } catch (Exception e) { logger.error("Could not set user authentication in security context", e); } } response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); return false; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { // 可选:处理请求后的逻辑 } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { // 可选:处理请求完成后的逻辑 } }
四、总结
通过这次对登录认证校验问题的探讨,我们遵循了发现问题、查找原因、分析方案和解决问题的思路。首先,通过系统测试暴露了登录认证校验的漏洞,意识到需要更加安全的认证机制;然后在对比分析Cookie、Session和JWT等方案后,选择了更加适合分布式环境的JWT方案。最终,通过Filter和Intercept机制实现了基于JWT的认证拦截功能。