简单登录验证的实现

简单登录验证的实现

登录验证,对应英文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机制。