SpringBoot:SpringBoot:实战项目TILAS智能学习辅助系统1.3

05-13 1103阅读

登录认证

需求:输入登录请求服务器判断用户的用户名和密码
//控制层
@PostMapping("/login")
    public Result login(@RequestBody Emp emp);
@Override
    public Result login(Emp emp) {
        Emp emp1 = empService.selectLogin(emp);
        if(emp1 == null){
            System.out.println("用户名或密码错误");
            return Result.error("登录失败");
        }else{
            System.out.println("登录成功");
            Map map = new HashMap();
            map.put(emp1.getUsername(),emp1.getPassword());
            String secret = JWTUtils.generateJwt(map);
            return Result.success(secret);
        }
    }
//业务层
Emp selectLogin(Emp emp);
@Override
    public Emp selectLogin(Emp emp) {
        return empMapper.selectLogin(emp);
    }
//持久层
@Select("select * from emp where username = #{username} and password = #{password}")
    Emp selectLogin(Emp emp);

过滤:

如果没有过滤,用户可以直接通过链接直接访问功能,绕过登录.

所以我们需要

将登录成功的信息进行保存和封装记录成为登录成功的标记.

进行判断和拦截

使用

Filter过滤器和Interceptor拦截器

但是因为HTTP是无状态的,不能在多次请求之间共享数据,所以我们需要使用会话跟踪技术解决

会话跟踪技术解决

会话:

用户打开浏览器,访问web服务器的资源,会话建立,直到一方断开连接结束会话.

在一次会话中可以包含多次请求和响应

从浏览器发出请求到服务端,服务端再响应数据给前端,就完成了一次会话的建立

如果建立会话后,浏览器和服务端都没有被关闭,就会持续会话直到一方结束

中途可以一直使用该会话进行请求的发送和响应

会话跟踪:

一种维护浏览器的方法,服务器需要识别多次请求是否来自于同一浏览器,以便于在同一次会话的多次请求间共享数据

服务器会收到多个请求,多个请求可能来自于多个浏览器

所以浏览器需要进行以下操作

使用会话跟踪来进行识别
识别请求是否来自于同一个浏览器
识别浏览器后在同一个会话中多次请求间共享数据
实现会话跟踪
客户端会话跟踪技术:Cookie
服务端会话跟踪技术:Session

都可以实现会话跟踪,但Cookie存储在浏览器端,而Session是存储在服务端

请求时创建唯一id的session保存在服务端,响应时封装成cookie在响应头中返回给客户端

SpringBoot:SpringBoot:实战项目TILAS智能学习辅助系统1.3

 
传统会话跟踪的问题(服务器集群,客户端多样化)
服务器集群:

服务器的并发访问量有限,需要通过代理服务器来分配访问给多个服务器(集群中数据无法共享)

问题主要体现在两个方面

服务器集群环境下Session的共享问题
移动端APP端无法使用Cookie

所以我们使用

令牌技术

登录请求时,如果登录成功可以给前端响应一个令牌(一个特殊的字符串,代表每个用户合法的身份凭证)

前端将登录返回的令牌记录下来保存在自己的客户端

在后续的请求中每次请求都会携带该令牌,在之后服务端使用Filter或Interceptor对所有请求进行拦截并校验,获取请求中携带的令牌进行判断,如果合法就放行,如果不合法就返回错误信息并跳转到登录页面.

解决了集群环境下的认证问题,减轻服务器的存储压力
支持PC端,移动端

JWT令牌

JSON Web Token

一个开发的行业标准,定义了一种简介的,自包含的协议格式,用于在通信双方传递JSON对象,传递的信息经过数字签名可以被验证和信任.

分为三个部分

Header(头)

记录令牌类型,签名算法等,使用Base64编码

Payload(有效载荷)

携带用户信息和过期信息,使用Base64编码

Signature(签名)

防止Token被篡改,确保安全性,是一个字符串

使用秘钥加密

校验时的签名秘钥必须与生成令牌时的秘钥一致.
如果JWT令牌解析校验时报错,说明JWT令牌被篡改或失效了,令牌非法.

使用json进行数据传输,通用型广泛,体积小,便于传输

无需在服务器端保存相关信息

jwt载荷部分可以存储业务相关信息(不能是敏感信息)

生成令牌

public class JWTUtils {
    private static String signKey = "cfjg";
//    private static Long expire = 43200000L;
    public static String generateJwt(Map claims){//生成令牌
        String jwt = Jwts.builder().addClaims(claims)
                //添加数据
                .signWith(SignatureAlgorithm.HS256,signKey)
                //设置算法
                .setExpiration(new Date(System.currentTimeMillis() + 1000L*3*60))
                //设置过期时间
                .compact();
                //生成
        return jwt;
    }
    public static Claims parseJWT(String jwt){
        Claims claims = Jwts.parser()
                .setSigningKey(signKey)
                //设置秘钥
                .parseClaimsJws(jwt)
                //解析令牌
                .getBody();
                //获取数据
        return claims;
    }
}

过滤器Filter

JavaWeb三大组件之一(Servlet,Filter,Listener)

底层使用动态代理,对请求进行拦截和处理

对token进行判断和解析,如果失败就跳转回login,如果成功就正常访问

public class Filter implements javax.servlet.Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        //请求对象,响应对象,拦截对象
        HttpServletRequest httpServletRequest = (HttpServletRequest)servletRequest;
        HttpServletResponse httpServletResponse = (HttpServletResponse)servletResponse;
        String url = httpServletRequest.getRequestURI();
        System.out.println(url);
        if(url.matches(".?login.?")){
            System.out.println("login:登录页面");
            filterChain.doFilter(httpServletRequest,httpServletResponse);
            return;
        }else{
            String token = httpServletRequest.getHeader("token");
            if(token == null || token.equals("")){
                httpServletResponse.getWriter().write(JSONObject.toJSONString(Result.error("NOT_LOGIN")));
                System.out.println("令牌错误");
                return;
            }
            try{
                JWTUtils.parseJWT(token);
                System.out.println("令牌正确");
                filterChain.doFilter(httpServletRequest,httpServletResponse);
                return;
            }catch (Exception e){
//                filterChain.doFilter(httpServletRequest,httpServletResponse);
                httpServletResponse.getWriter().write(JSONObject.toJSONString(Result.error("NOT_LOGIN")));
                System.out.println("令牌错误");
                return;
            }
        }
    }
}

Interceptor拦截器

注册拦截器
@Configuration
public class InterceptorRegist implements WebMvcConfigurer {//注册拦截器
    @Autowired
    LoginInterceptor loginInterceptor;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginInterceptor).addPathPatterns("/**");
    }
}
使用拦截器
package com.example.tlias.Interceptor;
import com.alibaba.fastjson.JSONObject;
import com.example.tlias.pojo.Result;
import com.example.tlias.util.JWTUtils;
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 LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object handler) throws Exception {//过滤前
        String url = httpServletRequest.getRequestURI();
        System.out.println(url);
        if(url.matches(".?login.?")){
            System.out.println("interceptor running");
            return true;//放行
        }else{
            String token = httpServletRequest.getHeader("token");
            if(token == null || token.equals("")){
                httpServletResponse.getWriter().write(JSONObject.toJSONString(Result.error("NOT_LOGIN")));
                System.out.println("interceptor running");
                return false;//拦截
            }
            try{
                JWTUtils.parseJWT(token);
                System.out.println("interceptor running");
                return true;
            }catch (Exception e){
//                filterChain.doFilter(httpServletRequest,httpServletResponse);
                httpServletResponse.getWriter().write(JSONObject.toJSONString(Result.error("NOT_LOGIN")));
                System.out.println("interceptor running");
                return false;
            }
        }
    }
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {//过滤后
        System.out.println("方法执行结束");
    }
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {//请求结束
        System.out.println("请求结束");
    }
}

Filter和Interceptor的区别

接口规范的不同:过滤器要实现Filter接口,拦截器需要实现HandlerInterceptor接口

拦截范围不同:过滤器Filter会拦截所有资源,但Interceptor只会拦截Spring环境中的资源

SpringBoot:SpringBoot:实战项目TILAS智能学习辅助系统1.3

异常处理

在SpringBoot项目中的异常如果从控制层向上抛出会暴露给用户,这是不被允许的.

处理异常的方案

1,在Controller中使用trycatch进行处理(过于冗长)

2,全局异常处理器(推荐)

由SpringMVC提供,接收所有Controller中产生的异常,一般在exception包下定义

@RestControllerAdvice//相当于@ResponseBody+@ControllerAdvice
public class GlobalExceptionHandler {//全局异常处理器
    @ExceptionHandler(Exception.class)//接收需要处理的异常的字节码文件
    public Result exceptionHandler(Exception exception){
        return Result.error(exception.getMessage());
    }
}
VPS购买请点击我

文章版权声明:除非注明,否则均为主机测评原创文章,转载或复制请以超链接形式并注明出处。

目录[+]