SpringBoot---登录校验

1. 登录功能

登录服务端的核心逻辑
接收前端请求传递的 用户名 和 密码,然后再根据用户名和密码查询用户信息,如果用户信息存在,则说明用户输入的用户名和密码正确。如果查询到的用户不存在,则说明用户输入的用户名和密码错误。
SpringBoot---登录校验_第1张图片

思路:
分别创建一个LoginController层,因为Mapper层对应的是Emp类所以Mapper层使用EmpMapper
在LoginController层调用EmpService层的登录方法
在EmpService层则需创建一个EmpMapper层的成员变量,因为EmpService层分为功能接口和实现类,所以在实现类中重写接口中的登录方法,在实现类中调用Mapper层的成员变量使用登录方法
EmpMapper层调用数据库,使用注解的方法,简化代码

代码实现

@RestController
public class LoginController {

    @Autowired
    private EmpService empService;

    @PostMapping("/login")
    public Result login(@RequestBody Emp emp){
        Emp e = empService.login(emp);
	    return  e != null ? Result.success():Result.error("用户名或密码错误");
    }
}
/**
* 登录
* EmpService接口
* @param emp
*/
Emp login(Emp emp);
//EmpServiceImpl
@Override
public Emp login(Emp emp) {
    Emp e = empMapper.getByUsernameAndPassword(emp);
    return e;
}
//根据用户名及密码查询员工信息
@Select("select * from emp where username = #{username} and password = #{password}")
Emp getByUsernameAndPassword(Emp emp);

2. 登录校验

1.会话技术

SpringBoot---登录校验_第2张图片
当我们成功登录后,仅在login界面根据 用户名 和 密码查询用户,并没有在服气端或客户端记录到登录成功的信息,就会导致我们直接访问/depts和/emps还是不需要输入用户名和密码就能直接访问。
而HTTP协议又是无状态协议,那下一次在请求时,无法判断员工是否已经登陆。

SpringBoot---登录校验_第3张图片
而要想解决上述的问题呢,我们就需要做两件事情:

● 在员工登录成功后,需要将用户登录成功的信息,存起来,记录用户已经登录成功的标记。
● 在浏览器发起请求时,需要在服务端进行统一拦截,然后读取登录标记中的信息,如果有登录成功的信息,就说明用户登录成功,放行请求,如果发现登录标记中没有登录成功的标记,则给前端返回错误信息,跳转至登录页面。

统一拦截:
Filter过滤器 , Interceptor 拦截器
登录标记:
就需要用户登录成功之后,每一次请求中,都可以获取该标记。
此标记需要在用户登录成功之后,每一个请求资源中,都可以获取到,也就是说可以在多次请求间共享。但是我们之前介绍过,HTTP是无状态的,不能在多次请求间共享数据,所以,此处需要使用会话跟踪技术来解决。

会话跟踪

先理解一下什么是回话?什么是跟踪?

会话
用户打开浏览器,访问web服务器的资源,会话建立,直到有一方断开连接,会话结束
在一次会话中可以包含多次请求和响应
○ 从浏览器发出请求到服务端,响应数据给前端之后,一次会话(在浏览器和服务器之间)就建立了。
○ 会话被建立后,如果浏览器或服务端都没有被关闭,则会话就会持续建立着。
○ 浏览器和服务器就可以继续使用该会话进行请求发送和响应,上述的整个过程就被称之为会话。

思考:下图中总共建立了几个会话 ?

SpringBoot---登录校验_第4张图片

每个浏览器都会与服务端建立了一个会话,加起来总共是3个会话。

会话跟踪
一种维护浏览器状态的方法,服务器需要识别多次请求是否来自于同一浏览器,以便在同一次会话的多次请求间共享数据
○ 服务器会收到多个请求,这多个请求可能来自多个浏览器,如上图中的5个请求来自3个浏览器。
○ 服务器需要用来识别请求是否来自同一个浏览器。
○ 服务器用来识别浏览器的过程,这个过程就是会话跟踪。
○ 服务器识别浏览器后就可以在同一个会话中多次请求之间来共享数据。

那么该如何实现会话跟踪技术呢?

  1. 客户端会话跟踪技术:Cookie
  2. 服务端会话跟踪技术: Session
    两个技术都可以实现会话跟踪,
    他们之间最大的区别:Cookie是存储在浏览器端,Session是存储在服务器端

SpringBoot---登录校验_第5张图片

Cookie、Session主要问题,体现在两个方面:
● 服务端集群环境下Session的共享问题。
● 移动端APP端无法使用Cookie。

如何来解决上述的问题呢?

令牌技术

SpringBoot---登录校验_第6张图片
使用令牌技术,具体流程如下:

● 在进行登录请求时,如果用户登录成功,可以给前端响应一个令牌(其实就是一个特殊的字符串,就是一个用户合法的身份凭证)。
● 前端需要将登录返回的令牌记录下来,保存在浏览器端(客户端)。
● 该浏览器在后续的请求中,每一次请求都会将该令牌携带到服务端,然后接下来在服务端,我们可以通过Filter或Interceptor对所有的请求进行拦截,然后进行校验,获取到请求中携带过来的令牌,进行判断,如果令牌正确合法,则放行,如果令牌不合法,则直接返回错误信息给前端,前端跳转到登录页面。

令牌技术的优势
解决了集成环境下的认证问题,减轻服务器端的存储压力
支持PC 端和移动端

2.JWT令牌

JSON Web Token(JWT)是一个开放的行业标准(RFC 7519),它定义了一种简洁的、自包含的协议格式,用于在通信双方传递json对象,传递的信息经过数字签名可以被验证和信任。

SpringBoot---登录校验_第7张图片

2.1 令牌的生成与校验
生成

1). pom.xml 引入jwt的依赖

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>

2). 生成JWT代码实现

public class JwtDemo {

    @Test
    public void genJwt(){
        Map<String,Object> 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);
    }

}
校验

1). 代码实现

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

注意事项
● JWT校验时使用的签名秘钥,必须和生成JWT令牌时使用的秘钥是配套的。
● 如果JWT令牌解析校验时报错,则说明 JWT令牌被篡改 或 失效了,令牌非法。

2.2 登录-生成令牌

怎么在登录成功之后,生成JWT令牌并返回给浏览器?

● 步骤:
○ 引入JWT工具类
○ 登录完成后,调用工具类生成JWT令牌并返回

//引入JWT工具类
public class JwtUtils {
	
    private static String signKey = "itheima";
    private static Long expire = 43200000L;
	
    /**
     * 生成JWT令牌
     * @param claims JWT第二部分负载 payload 中存储的内容
     * @return
     */
    public static String generateJwt(Map<String, Object> claims){
        String jwt = Jwts.builder()
                .addClaims(claims)
                .signWith(SignatureAlgorithm.HS256, signKey)
                .setExpiration(new Date(System.currentTimeMillis() + expire))
                .compact();
        return jwt;
    }
	
    /**
     * 解析JWT令牌
     * @param jwt JWT令牌
     * @return JWT第二部分负载 payload 中存储的内容
     */
    public static Claims parseJWT(String jwt){
        Claims claims = Jwts.parser()
                .setSigningKey(signKey)
                .parseClaimsJws(jwt)
                .getBody();
        return claims;
    }
}
//登录成功,生成JWT令牌并返回
@RestController
public class LoginController {
	
    @Autowired
    private EmpService empService;
	
    @PostMapping("/login")
    public Result login(@RequestBody Emp emp){
        Emp e = empService.login(emp);
        if(e != null){ //用户名密码正确
            Map<String,Object> claims = new HashMap<>();
            claims.put("id",e.getId());
            claims.put("username",e.getUsername());
            claims.put("name",e.getName());

            //生成JWT令牌
            String jwt = JwtUtils.generateJwt(claims);
            return Result.success(jwt);
        }
        return Result.error("用户名或密码错误");
    }
}

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

2.3 过滤器Filter

● 概念:Filter 过滤器,
是 JavaWeb 三大组件(Servlet、Filter、Listener)之一。
● 过滤器可以把对资源的请求拦截下来,从而实现一些特殊的功能。
● 过滤器一般完成一些通用的操作,比如:登陆鉴权、统一编码处理、敏感字符处理等等…

执行流程
SpringBoot---登录校验_第8张图片

疑问:
● 放行后访问对应资源,资源访问完成后,还会回到Filter中吗? 答案:会
● 如果回到Filter中,是重新执行还是执行放行后的逻辑呢?答案:执行放行后逻辑

Filter 拦截路径
SpringBoot---登录校验_第9张图片

登录校验Filter

SpringBoot---登录校验_第10张图片

登录完成后,会把JWT令牌返回给前端,前端浏览器会将其存入本地存储。 在后面的请求中,前端会自动在请求头中将令牌token携带到服务端,接下来呢,我们就需要在服务端中通过过滤器来进行统一拦截校验。 过滤器中具体的校验流程如下:

SpringBoot---登录校验_第11张图片

● 获取请求url。
● 判断请求url中是否包含login,如果包含,说明是登录操作,放行。
● 获取请求头中的令牌(token)。
● 判断令牌是否存在,如果不存在,返回错误结果(未登录)。
● 解析token,如果解析失败,返回错误结果(未登录)。
● 放行。

2.4 拦截器Interceptor

拦截器:是一种动态拦截方法调用的机制,类似于过滤器。在SpringMVC中动态拦截控制器方法的执行
作用:在指定的方法调用前后执行预先设定的代码,完成功能增强

SpringBoot---登录校验_第12张图片
执行流程
SpringBoot---登录校验_第13张图片

SpringBoot---登录校验_第14张图片

登录校验Interceptor

SpringBoot---登录校验_第15张图片

● 获取请求url。
● 判断请求url中是否包含login,如果包含,说明是登录操作,放行。
● 获取请求头中的令牌(token)。
● 判断令牌是否存在,如果不存在,返回错误结果(未登录)。
● 解析token,如果解析失败,返回错误结果(未登录)。
● 放行。

Filter 过滤器 与 Interceptor 拦截器 区别

● 接口规范不同:过滤器需要实现Filter接口,而拦截器需要实现HandlerInterceptor接口。
● 拦截范围不同:过滤器Filter会拦截所有的资源,而Interceptor只会拦截Spring环境中的资源。

问题:

1.登录校验的实现思路是怎样的?

分别创建一个LoginController层,EmpService层的类,因为Mapper层对应的是Emp类所以Mapper层使用EmpMapper
在LoginController层调用EmpService层的登录方法
在EmpService层则需创建一个EmpMapper层的成员变量,因为EmpService层分为功能接口和实现类,所以在实现类中重写接口中的登录方法,在实现类中调用Mapper层的成员变量使用登录方法
EmpMapper层调用数据库,使用注解的方法,简化代码

2会话技术有哪些方式可以实现?

session、cookie 令牌

3JWT令牌组成部分有哪些,各自作用是什么?

Header 头、记录令牌类型、签名算法等 Payload 有效载荷、携带一些用户信息及过期时间
Signature 签名、防止Token被篡改、确保安全性

4.怎么使用JWT令牌?(依赖,创建,校验)

1). pom.xml 引入jwt的依赖
2). 生成JWT代码实现
3). 代码实现

5.项目中在什么时候去生成令牌?

登录成功后

6.当前端携带令牌访问资源时怎么去拦截校验令牌的合法性?

过滤器 filter 和拦截器 Interceptor

7,过滤器具体使用的步骤是怎样的?

1). 定义类,实现 Filter接口,并重写doFilter方法
2). 配置Filter拦截资源的路径:在类上定义 @WebFilter 注解
3). 在doFilter方法中输出一句话,并放行
4). 在引导类上使用@ServletComponentScan 开启 Servlet 组件扫描

8拦截器具体使用的步骤是怎样的?

  1. 定义拦截器,实现HandlerInterceptor接口,并重写其所有方法
  2. 注册拦截器

9.项目中异常是怎么处理的?具体怎么实现?

● 方案一:在Controller的方法中进行try…catch处理 (代码过于臃肿)
● 方案二:全局异常处理器

状态码为 500,代表服务端错误,服务端出现了异常

● dao、service 层向上抛即可
● 但 controller 层不同,它如果再向上抛,此异常必然暴露给最终用户,这是不允许的。

Exception异常分类
- 运行时异常 : RuntimeException , 编译时无需处理 .
- 编译时异常 : 非 RuntimeException , 编译时处理 .

10任务:把登录校验功能实现完善,并集成全局异常处理

你可能感兴趣的:(spring,boot,后端,java)