JavaWeb学习——登录校验

JavaWeb学习——登录校验

一、功能实现

只需要接受请求参数username和password,然后调用接口在数据库表中查询键值匹配的数据项即可

二、登录校验

登录校验通常分为两步,一是登录标记,二是统一拦截

1、会话跟踪

会话:用户打开浏览器,访问web服务器的资源,会话建立,知道有一方断开连接,会话结束。在一次会话中可以包含多次请求和响应

会话跟踪:一种维护浏览器的方法,服务器要识别多次请求是否来自于同一浏览器,以便于在同义词会话的多次请求间共享数据。主要有Cookie、Session、JWT令牌技术。

1.1 Cookie方案:

Cookie是存储在客户端服务器的,客户端(浏览器)第一次访问服务器,会在服务器设置一个cookie,服务器响应时会将响应头set-cookie(设置有cookie数据)返回给浏览器,浏览器会将cookie的内容(name=value)存储在浏览器本地。之后浏览器每次想服务器发出请求,都会自动携带cookie,服务器判断出cookie存在,则可以基于cookie在同义词会话的不同请求之间共享数据。

优点:HTTP协议支持的技术,无需手动操作数据的解析和携带等

缺点:移动端APP不适用;不安全,用户可在浏览器禁用cookie;cookie不能跨域(协议、ip/域名、端口)

1.2 Session方案:

session是存储在服务端的,底层依然是基于cookie来实现。浏览器第一次请求服务器,那么会话对象不存在,此时服务器会自动创建会话对象session,并设置一个ID,然后响应头set-cookie对应的的内容为(JSSSIONID = ID),浏览器会自动识别响应头,将cookie存到本地。后续的每次浏览器请求,都会携带cookie到服务器,服务器就可以找到对应lD的会话对象session

优点:浏览器获取不到session对象,较安全

缺点:服务器集群的环境不能直接使用;实质上还是cookie,因此不适合移动端、用户可以禁用、不能跨域

1.3 JWT(JSON Web Token):

由程序员实现令牌的生成、传递与校验

根据自定义令牌获取规则,客户端会生成的JWT令牌返回给浏览器,之后浏览器会在请求头中携带一个token,token的值就是令牌的BASE64编码(是编码,不是加密),令牌原本为JSON格式,包括头部(算法、类型),负载(name、ID等属性),数字签名三个部分。当请求头传递的token为空或无效,则会被拦截。注:令牌是要设置有效期限的。

以登录校验为例:

生成JWT
/**
     * 生成JWT令牌
     * @param claims JWT第二部分负载 payload 中存储的内容
     * @return
     */
    public static String generateJwt(Map claims){
        String jwt = Jwts.builder()
                .addClaims(claims)
                .signWith(SignatureAlgorithm.HS256, signKey)
                .setExpiration(new Date(System.currentTimeMillis() + expire))
                .compact();
        return jwt;
    }
//  使用
    @PostMapping("/login")
    public Result Login(@RequestBody Emp emp){
        log.info("员工登录:{}",emp);
        Emp e =  empService.login(emp);
        //登录成功,生成令牌并下发
        if(e!=null){
            Map claims = new HashMap<>();
            claims.put("id", e.getId());
            claims.put("name", e.getName());
            claims.put("username", e.getUsername());
            String jwt = JwtUtils.generateJwt(claims);//jwt包含了当前登录的员工信息
            return Result.success(jwt);
        }
        //登录失败,返回错误信息
        return Result.error("用户名或密码错误");
    }
获取并解析令牌
/**
     * 解析JWT令牌
     * @param jwt JWT令牌
     * @return JWT第二部分负载 payload 中存储的内容
     */
    public static Claims parseJWT(String jwt){
        Claims claims = Jwts.parser()
                .setSigningKey(signKey)
                .parseClaimsJws(jwt)
                .getBody();
        return claims;
    }
// 获取并解析(片段)
String jwt = request.getHeader("token");
        if (!StringUtils.hasLength(jwt)){
            log.info("请求头token为空,返回未登录信息");
            Result error = Result.error("NOT_LOGIN");
            String notLogin = JSONObject.toJSONString(error);
            response.getWriter().write(notLogin);
            return false;
        }
        try {
            JwtUtils.parseJWT(jwt);
        }catch (Exception e){
            e.printStackTrace();
            log.info("令牌解析失败,返回未登录信息");
            Result error = Result.error("NOT_LOGIN");
            String notLogin = JSONObject.toJSONString(error);
            response.getWriter().write(notLogin);
            return false;
        }

2、拦截技术

2.1 基于servlet的Filter

核心是在Filter接口的doFilter()函数,doFilter()函数中的chain.doFilter(request,response)实现放行,在其前后实现放行前逻辑和放行后逻辑

通过@WebFilter(urlPatterns = "/*")注解设置过滤范围,"/*"表示全过滤,也可以是"/emp",这样只拦截到emp这一级

通常可以用过滤器类名来确定优先级(A>B),也可以设置@Order()注解确定顺序。过滤时,先执行AFilter过滤前逻辑,再执行BFilter过滤前逻辑,然后放行完成Controller中的任务,之后执行BFilter过滤后逻辑,而后是A……

我们可以在过滤器执行前逻辑编写,查看并校验token的代码,来进行登录校验

2.2 基于springboot的Interceptor

与过滤器功能相似,由于是springboot框架提供的方法,因此拦截时是先完成filter才进行interceptor的拦截

其核心是HandlerInterceptor接口的preHandler方法,定义放行逻辑,通过则返回true,不通过则返回false

@Component
public class LoginCheckInterceptor implements HandlerInterceptor {
    @Override //目标方法运行前执行,返回为true则放行,false拦截
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 实现放行前逻辑
        return true;
    }
​
    @Override // 目标方法运行后执行
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle……");
    }
​
    @Override // 视图渲染完毕后运行
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion……");
    }
}
​

需要定义一个configuration的类,实现WebMvcConfigurer接口的addInterceptor方法来控制拦截范围

//与filter不同,Interceptor的"/*"只拦截本级目录,而"/**"才能拦截全部目录
//配置类如下
@Configuration
public class WebConfig  implements WebMvcConfigurer {
    @Autowired
    private LoginCheckInterceptor loginCheckInterceptor;
​
    @Override
    public void addInterceptors(InterceptorRegistry registry){
        registry.addInterceptor(loginCheckInterceptor).addPathPatterns("/**").excludePathPatterns("/login");
    }
}

你可能感兴趣的:(JavaWeb基础学习,spring,tomcat,servlet,后端)