简单登录验证的实现
简单登录验证的实现
登录验证,对应英文Authentication。
这里的简单登录验证,指的是使用HTTP协议的情况下,传统浏览器-服务器通信时,广泛使用的cookie-session登录验证机制。
登录验证的要求
除首页、login页面、js脚本文件、css样式文件外的资源,都需要用户登录后,才能访问。
思路
利用cookie-session作登录验证。cookie-session实现了在无状态的HTTP协议下实现状态的保存。
HTTP协议是无状态的。为了识别同一个浏览器发出的请求,发展出了cookie-session机制。
打开浏览器,浏览器第一次向服务器发出HTTP请求,服务器端就会生成一个session对象(可以用来保存该浏览器/用户的状态)。在生成HTTP响应时,服务器会一并将这个session的id写到HTTP响应头的Set-Cookie域中,传给浏览器。
浏览器收到响应后,会记录这个session-id,之后向同一个服务器/网站发起请求时,都会在HTTP请求头的Cookie域中带上这个session-id。服务器收到这样的请求,就会知道,这是来自同一个浏览器的请求。因此可以根据该浏览器的
浏览器关闭时,保存的所有网站的cookie都被清除,而服务器上生成的session对象在一定时间后也会被清除。
用户校验完账号密码,完成登录时,向其session中添加用户登录信息,比如向其session中添加用户名。
检查每个请求对应的session中是否包含了登录状态信息,如果未包含登录信息,跳转到登录页面。当然,这里要排除掉访问login页面、公共页面、静态公共资源的请求。
这里是java后端环境,使用servlet web容器,可以使用servlet Filter。 如果还使用了Spring开发项目,Spring的Interceptor可以做到更细粒度的拦截行为。Spring的interceptor有Spring aop的MethodInterceptor和SpringMVC的HandlerInterceptor。
实现
使用SpringMVC的HandlerInterceptor来拦截请求,作登录验证。
实现拦截器
package com.jingmin.logindemo.interceptor;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
@Component
public class SecurityInterceptor implements HandlerInterceptor {
public static final String SESSION_KEY = "user";
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HttpSession session = request.getSession();
if (session.getAttribute(SESSION_KEY) != null) {
return true;
}
// 跳转登录
String url = "/login.html";
response.sendRedirect(url);
return false;
}
}
在SpringMVC中注册Interceptor
@Configuration
public class LoginDemoApplication {
@Bean
public WebMvcConfigurer webMvcConfigurer(SecurityInterceptor securityInterceptor) {
return new WebMvcConfigurer() {
@Override
public void addInterceptors(InterceptorRegistry registry) {
InterceptorRegistration interceptorRegistration = registry.addInterceptor(securityInterceptor);
interceptorRegistration.excludePathPatterns("/", "/login*", "/index*");
interceptorRegistration.addPathPatterns("/**");
}
};
}
}
这里是向SpringMVC中注册WebMvcConfigurer,并在其中添加Interceptor的方法。
如果SpringMVC使用xml配置文件,如下的配置可以实现同样的效果:
<!-- 配置拦截器-->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path="/"/>
<mvc:exclude-mapping path="/login*"/>
<mvc:exclude-mapping path="/index*"/>
<bean class="com.jingmin.interceptor.SecurityInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
登录校验并写入登录信息到session
@Controller
public class LoginController {
@Autowired
UserService userService;
@PostMapping("loginAuth")
@ResponseBody
public WebResult<String> login(@RequestParam String email,
@RequestParam String password,
HttpSession session) {
boolean isSuccess = userService.userAuthentication(email, password, session);
WebResult<String> webResult =null;
if(isSuccess) {
webResult = new WebResult<String>(1, "登录成功");
}else {
webResult = new WebResult<>(10, "用户名或密码错误");
}
return webResult;
}
}
@Service
public class UserService {
@Autowired
UserDao userDao;
public boolean userAuthentication(String email, String password, HttpSession session) {
User user = userDao.getUserByEmailAndPassword(email, password);
if(user!=null) {
session.setAttribute(SecurityInterceptor.SESSION_KEY, user.getId());
}
return user != null;
}
}
参考
https://www.liaoxuefeng.com/wiki/1252599548343744/1347180610715681
https://zhuanlan.zhihu.com/p/78447141
https://blog.csdn.net/huang906391/article/details/78376766
https://www.cnblogs.com/GoodHelper/p/6343190.html
局限性
cookie-session机制的登录验证无法预防CSRF攻击。如果你登录了网站A,又去访问网站B,如果网站B上的网页中存在网站A的危险操作链接,当你不小心/被引诱点击时,发往A的请求自动带上了A的cookie,网站A会认为这是你在登录状态下做出的行为。比如给个美女图片,点一下却是银行转账操作请求,虽然正好登录了银行网站的可能性很小。(只作假设,银行不会这么干)。前后端不分离,使用模板的项目中,可以在渲染页面的过程中,在页面中加入一个服务端生成的随机数,进行危险操作时需要带上这个随机数,可以解决CSRF问题,但是并不优雅。
cookie-session机制的登录验证无法适应当前多平台登录的要求。除了浏览器,还有手机APP等客户端,而手机APP是无法使用cookie的。也就无法使用cookie-session机制完成登录。如果想实现多平台使用同一套接口,就得考虑其他方式。比如使用token机制。