SpringBoot登录校验

1.登录校验的实现

(1)统一拦截:可以使用两种技术实现,Filter过滤器  以及 Interceptor 拦截器。

(2)登录标记:就需要用户登录成功之后,每一次请求中,都可以获取到该标记。

2.会话技术

(1)客户端会话跟踪技术:Cookie

(2)服务端会话跟踪技术:Session

        这两个技术都可以实现会话跟踪,它们之间最大的区别:Cookie是存储在浏览器端,而Session是存储在服务器端

        在现今前前后端分离开发模式下,Cookie、Session这种会话技术已很少使用,而且在服务器集群环境下 以及 客户端多样化的情况下,传统的Cookie、Session的会话方案就显得力不从心了,其主要问题,体现在两个方面:

(1)服务端集群环境下Session的共享问题。

(2)移动端APP端无法使用Cookie。

3.令牌

优势:

(1)解决了集群环境下的认证问题,减轻服务器端的存储压力

(2)支持PC端、移动端

4.JWT令牌

1.优势:

(1)使用 json 作为数据传输,有广泛的通用型,并且体积小,便于传输

(2)不需要在服务器端保存相关信息

(3)jwt 载荷部分可以存储业务相关的信息(非敏感的),例如用户信息、角色等

2.组成

(1)第一部分:Header(头),作用:记录令牌类型、签名算法等

{
	"alg":"HS256",
	"type":"JWT"
}

SpringBoot登录校验_第1张图片

(2)第二部分:Payload(有效载荷),作用:携带一些用户信息及过期时间等

SpringBoot登录校验_第2张图片

{
	"id":"1",
	"username":"Tom"
}

(3)第三部分:Signature(签名),作用:防止Token被篡改、确保安全性

HMACSHA256(
    base64UrlEncode(header) + "." +
    base64UrlEncode(payload),
    secret
)

base64UrlEncode(header):jwt令牌的第一部分。
base64UrlEncode(payload):jwt令牌的第二部分。
secret:签名所使用的密钥

        签名算法是一种用于验证数字签名的算法,它可以确保消息的完整性和真实性。签名算法通常用于数字证书和数字签名的验证,以确保通信双方之间的身份和消息的完整性。常见的签名算法包括RSA、DSA、ECDSA、EdDSA等。这些算法都使用数学原理来生成数字签名,以确保签名的可信度和唯一性。RSA算法是最常见的数字签名算法之一,它使用公钥加密和私钥解密来生成数字签名。DSA和ECDSA算法也被广泛使用,它们使用椭圆曲线密码学来生成数字签名。EdDSA算法是一种新的数字签名算法,它使用一种新的哈希函数和椭圆曲线密码学来生成数字签名。签名算法的安全性取决于使用的哈希函数和加密算法的安全性。因此,签名算法的安全性通常需要进行充分的评估和测试,以确保它们能够提供足够的安全性。

3.生成

(1)在pom包中加入依赖


    io.jsonwebtoken
    jjwt
    0.9.1

(2)生产JWT令牌代码

public class JwtDemo {

    @Test
    public void genJwt(){
        Map claims = new HashMap<>();
        claims.put("id",1);
        claims.put("username","Tom");

        String jwt = Jwts.builder()
                .setClaims(claims) //执行第二部分负载, 存储的数据
                .signWith(SignatureAlgorithm.HS256, "itheima") //签名算法及秘钥
                .setExpiration(new Date(System.currentTimeMillis() + 12*3600*1000)) //设置令牌的有效期
                .compact();
        System.out.println(jwt);
    }

}

(3)校验

    @Test
    public void parseJwt(){
        Claims claims = Jwts.parser()
                .setSigningKey("itheima")
            				.parseClaimsJws("eyJhbGciOiJIUzI1NiJ9.eyJpZCI6MSwiZXhwIjoxNjU5OTk1NTE3LCJ1c2VybmFtZSI6IlRvbSJ9.EUTfeqPkGslekdKBezcWCe7a7xbcIIwB1MXlIccTMwo")
                .getBody();
        System.out.println(claims);
    }

注意事项:

(1)JWT校验时使用的签名秘钥,必须和生成JWT令牌时使用的秘钥是配套的

(2)如果JWT令牌解析校验时报错,则说明 JWT令牌被篡改 或 失效了,令牌非法

4.使用

令牌一般在登陆时生成,登录成功之后,前端会在后面的每一次请求中将令牌携带过来,那接下来,我们需要做的就是需要在服务端统一拦截校验JWT令牌。

统一拦截请求,在服务端,我们可以通过两种手段实现:过滤器Filter、拦截器Interceptor

5.过滤器Filter

(创建filter包,在filter包里创建类定义过滤器)

1.介绍:

(1)概念:Filter 过滤器,是 JavaWeb 三大组件(Servlet、Filter、Listener)之一

(2)过滤器可以把对资源的请求拦截下来,从而实现一些特殊的功能

(3)过滤器一般完成一些通用的操作,比如:登陆鉴权、统一编码处理、敏感字符处理等等…

2.使用:

(1)定义类,实现 Filter接口,并重写doFilter方法

(2)配置Filter拦截资源的路径:在类上定义 @WebFilter 注解

(3)在doFilter方法中输出一句话,并放行

@WebFilter(urlPatterns = "/*")
public class DemoFilter implements Filter {
    /**
     *
     * @param servletRequest 负责处理客户端发送过来的请求(获取参数)
     * @param servletResponse 负责服务端响应数据给客户端
     * @param filterChain 过滤链,实现是否进行拦截(截停,放行)
     * @throws IOException
     * @throws ServletException
     */
	@Override
    public void doFilter(ServletRequest servletRequest , ServletResponse servletResponse , FilterChain filterChain ) throws IOException, ServletException {
        System.out.println("拦截方法执行, 拦截到了请求 ...");
        System.out.println("执行放行前逻辑 ...");

        filterChain.doFilter(servletRequest, servletResponse);

        System.out.println("执行放行后逻辑 ...");
    }
}

4). 在引导类(运行的)上使用@ServletComponentScan 开启 Servlet  组件扫描

3.执行流程

SpringBoot登录校验_第3张图片

放行后访问对应资源,资源访问完成后,还会回到Filter中

如果回到Filter中,执行放行后的逻辑

4.Filter 拦截路径

 ​​​​​

拦截路径

urlPattern值

含义

拦截具体路径

/login

只有访问 /login 路径时,才会被拦截

目录拦截

/emps/*

访问/emps下的所有资源,都会被拦截

拦截所有

/*

访问所有资源,都会被拦截

4.登录校验流程SpringBoot登录校验_第4张图片

(1)获取请求url。

(2)判断请求url中是否包含login,如果包含,说明是登录操作,放行。

(3)获取请求头中的令牌(token)。

(4)判断令牌是否存在,如果不存在,返回错误结果(未登录)。

(5)解析token,如果解析失败,返回错误结果(未登录)。

(怎么把errorResult对象转换成JSON字符串)

        //java对象  JSON对象之间的映射

        ObjectMapper objectMapper = new ObjectMapper();

       //把java对象转化为JSON字符串

        String json = objectMapper.writeValueAsString(errorResult); 

(6)放行。

@WebFilter(urlPatterns = "/*")
public class LoginCheckFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request , ServletResponse response , FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) request ;
        HttpServletResponse response = (HttpServletResponse) response ;

        //获取请求路径		
        String url = request.getRequestURL().toString();
        //如果是login, 直接放行
        if(url.contains("login")){
            System.out.println("登录操作, 直接放行...");
            filterChain.doFilter(req, res);
            return;
        }
		
        //如果不是 login ,需要校验 token
        String token = request.getHeader("token");
        if(!StringUtils.hasLength(token)){ //如果没有JWT令牌
            System.out.println("获取到token为空 , 返回错误信息...");
            //返回 未登录 提示信息
            String result = JSONObject.toJSONString(Result.error("NOT_LOGIN"));
            response.setContentType("application/json;charset=utf-8");
            response.getWriter().write(result);
            return ;
        }
		
        //解析jwt令牌, 如果解析失败, 则说明令牌无效 , 返回 未登录 提示信息
        try {
            JwtUtils.parseJWT(token);
            System.out.println("令牌解析成功, 直接放行 ...");
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("令牌解析失败 , 返回错误信息...");
			
            //返回 未登录 提示信息
            String result = JSONObject.toJSONString(Result.error("NOT_LOGIN"));
            response.setContentType("application/json;charset=utf-8");
            response.getWriter().write(result);
            return ;
        }
		
        //如果校验通过放行
        filterChain.doFilter(req, res);
    }

}

6.拦截器Interceptor

1.介绍

拦截器:(Interceptor)是一种动态拦截方法调用的机制,类似于过滤器。在SpringMVC中动态拦截控制器方法的执行

作用:在指定的方法调用前后执行预先设定的代码,完成功能增强

2.使用

1.定义拦截器,实现HandlerInterceptor接口,并重写其所有方法。(主要是重写preHandle方法)

(先定义interceport包,在interceport包里面创建类定义拦截器)

(主方法上加上@Component 把它注入容器)

@Component
public class LoginCheckInterceptor implements HandlerInterceptor {
    //目标资源方法执行前执行 , true : 放行 ; false : 不放行,拦截 ;
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
		System.out.println("preHandle ....");
        //如果校验通过放行
        return false;
    }


    //目标资源方法执行后执行
    @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 ....");
    }

}

2.注册拦截器

(定义config包,在config包里创建类注册拦截器) 

在主方法上加上@Configuretion 才能被识别到是配置类

(1)注入LoginCheckInterceptor(拦截器)

(2)重写addInterceptors方法 

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Autowired
    private LoginCheckInterceptor loginCheckInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginCheckInterceptor).addPathPatterns("/**");
    }
}

3.执行流程

SpringBoot登录校验_第5张图片

拦截路径 :

拦截路径

urlPattern值

含义

拦截具体路径

/login

只有访问 /login 路径时,才会被拦截

目录拦截

/emps/*

访问/emps下的下一级资源,如: /emps/1 ,但是 不会拦截 /emps/list/1,/emps/list/1/2

目录拦截

/emps/**

访问/emps下的所有资源,都会被拦截

拦截所有

/**

访问所有资源,都会被拦截

7.全局异常处理

为了方便我们程序员找到问题,我们会加入日志@Slf4j

@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

    /**
     * Exception异常分类
     *  - 运行时异常 : RuntimeException , 编译时无需处理 .
     *  - 编译时异常 : 非 RuntimeException , 编译时处理 .
     */
    @ExceptionHandler(Exception.class)
    public Result ex(Exception ex){
        ex.printStackTrace();
        log.error("全局异常处理",e)
        return Result.error("系统繁忙, 请稍后重试 ... ");
    }

}

你可能感兴趣的:(java,前端,服务器)