JavaWeb——后端之登录功能

2024-01-09 00:33:32

6. 登录功能

6.1 登录认证

只进行用户名和密码是否存在的操作

@Slf4j
@RestController
public class LoginController {
    @Autowired
    public EmpService empService;

    @PostMapping("/login")
    public Result login(@RequestBody Emp emp) {
        log.info("{}员工登录", emp);
        Emp e = empService.login(emp);
        return e != null? Result.success(): Result.error("用户名或密码错误");
    }
}

Emp login(Emp emp);

@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);

问题:在未登录的情况下,也可以直接访问部门管理、员工管理等功能

6.2 登录校验

请求来的时候线进行登录校验,校验通过的话,执行业务;不通过就跳转到登录界面

在这里插入图片描述

1)会话技术

会话: 浏览器和服务器ide一次连接,一次会话中可以包含多次请求和响应

会话跟踪: 服务器需要识别多次请求是否来自同一浏览器,以便在同一次会话的多次请求间共享数据

会话跟踪方案:

  • 客户端会话跟踪技术:Cookie
  • 服务端会话跟踪技术:Session
  • 令牌技术

Cookie:

①特点

  • 浏览器第一次发送请求给服务器的时候,服务器自动将Cookie返回给浏览器
  • 浏览器自动将收到的Cookie存储在本地
  • 浏览器向服务器发送请求的时候,自动携带Cookie

②优点

HTTP协议中支持的技术,浏览器自动进行

在这里插入图片描述

③缺点

  • 移动端APP无法使用Cookie
  • 不安全,用户可以自己禁用Cookie(在浏览器的设置里面)
  • Cookie不能跨域(协议、IP/域名、端口)

Session:

①特点

  • 基于Cookie

②优点

  • 存储在服务器端,安全

    在这里插入图片描述

③缺点

  • 服务器集群环境下无法直接使用Session
  • Cookie的缺点

令牌技术:

①特点

字符串的形式,可以存储在任何容器中,不只是可以存储在Cookie中

②优点

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

在这里插入图片描述

③缺点

  • 需要自己实现

2)JWT令牌

全称:JSON Web Token(https://jwt.io/)

定义了一种**简洁(就是字符串)**的、**自包含(可以在其中包括用户名等需要的字符串)**的格式,用于在通信双方以json数据格式安全地传输信息。由于数字签名的存在,这些信息是可靠的

组成:

第一部分:Header(头),记录令牌类型、签名算法等;采用Base64编码

第二部分:Payload(有效荷载),携带一些自定义信息、默认信息等;采用Base64编码

第三部分:Signature(签名),防止Token被篡改、确保安全性。将header、payload融入,并加入指定密钥,通过指定前面算法计算而来

例:

在这里插入图片描述

案例:

添加依赖

        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>
    // 生成JWTT
    @Test
    public void testGenJwt() {
        Map<String, Object> claims = new HashMap<>();
        claims.put("id", 1);
        claims.put("name", "tom");

        String jwt = Jwts.builder()
                .signWith(SignatureAlgorithm.HS256, "itheima")  // 签名算法和密钥
                .setClaims(claims)  // 自定义内容
                .setExpiration(new Date(System.currentTimeMillis() + 3600 * 1000))  // 设置有效期为1h
                .compact();
        System.out.println(jwt);
    }

    // 解析JWT
    @Test
    public void testParseJwt() {
        Claims claims = Jwts.parser()
                .setSigningKey("itheima")  // 密钥
                .parseClaimsJws("eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoidG9tIiwiaWQiOjEsImV4cCI6MTcwNDY5Njg1NH0.MzTlN_jrVSa7UJtTFoXw4xQ9-t4R7JHUmHeom5KQ2ss")
                .getBody();
        System.out.println(claims);
    }

错误

java.lang.NoClassDefFoundError: javax/xml/bind/DatatypeConverter

解决:

引入依赖

        <dependency>
            <groupId>javax.xml.bind</groupId>
            <artifactId>jaxb-api</artifactId>
            <version>2.3.1</version>
        </dependency>

登录-生成令牌

@Slf4j
@RestController
public class LoginController {
    @Autowired
    public EmpService empService;

    @PostMapping("/login")
    public Result login(@RequestBody Emp emp) {
        log.info("{}员工登录", emp);
        Emp e = empService.login(emp);
        // 登录成功,生成令牌,下发令牌
        if (e != null) {
            Map<String, Object> 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("用户名或密码错误");
    }
}

3)过滤器Filter

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

功能: 把对资源的请求拦截下来,从而实现一些特殊功能,例如:登录校验、统一编码处理、敏感字符处理等

实现:

  • 实现Filter接口
  • @WebFilter(urlPatterns=“/*”)
  • @ServletComponentScan——让启动类知道
@WebFilter(urlPatterns = "/*")  // 拦截所有请求
public class DemoFilter implements Filter {

    @Override  // 初始化方法,只调用一次
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("init初始化方法执行了");
    }

    @Override  // 拦截到请求之后调用
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("拦截到了请求");
        // 放行
        filterChain.doFilter(servletRequest, servletResponse);
    }

    @Override  // 销毁方法,只调用一次
    public void destroy() {
        System.out.println("destroy销毁方法执行了");
    }
}

执行流程:

在这里插入图片描述

拦截路径:

在这里插入图片描述

过滤器链:

一个web应用有中,可以配置多个过滤器,多个过滤器就形成了一个过滤器链

顺序——注解配置的Filter,优先级是按照过滤器类名(字符串)的自然排序

在这里插入图片描述

@Slf4j
@WebFilter(urlPatterns = "/*")
public class LoginCheckFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        // Tomcat传入的是request实际上是HTTPrequest,要进行强制类型转换
        HttpServletRequest req = (HttpServletRequest) servletRequest;
        HttpServletResponse resp = (HttpServletResponse) servletResponse;
        // 1. 获取请求url
        String url = req.getRequestURL().toString();
        log.info("请求的url:{}", url);

        // 2. 判断请求url中是否包含login,如果包含,说明是登录操作,放行
        if (url.contains("login")) {
            log.info("放行");
            filterChain.doFilter(servletRequest, servletResponse);
            return;
        }
        // 3. 获取请求头中的令牌(token)
        String jwt = req.getHeader("token");
        // 4. 判断令牌是否存在,如果不存在,返回错误结果(未登录)
        if (!StringUtils.hasLength(jwt)) {
            log.info("请求头token为空,返回未登录的信息");
            Result erro = Result.error("NOT_LOGIN");
            // 手动将Result对象转化为json格式——alibaba,fastJSON
            String notLogin = JSONObject.toJSONString(erro);
            resp.getWriter().write(notLogin);
            return;
        }
        // 5. 解析token,如果解析失败,返回错误结果(未登录)
        try {
            JwtUtils.parseJWT(jwt);
        } catch (Exception e) {
            e.printStackTrace();
            log.info("令牌解析失败,返回未登录错误信息");
            Result erro = Result.error("NOT_LOGIN");
            // 手动将Result对象转化为json格式——alibaba,fastJSON
            String notLogin = JSONObject.toJSONString(erro);
            resp.getWriter().write(notLogin);
            return;
        }
        // 6. 放行
        log.info("合法,放行");
        filterChain.doFilter(servletRequest, servletResponse);
    }
}

4)拦截器Interceptor

概念: 是一种动态拦截方法调用的机制,类似过滤器。Spring框架中提供的,用来动态拦截控制器方法的执行

作用: 拦截请求,在指定的方法调用前后,根据业务需要执行预先设定的代码

@Component
public class LoginCheckInterceptor implements HandlerInterceptor {
    @Override  // 目标资源方法运行前运行,返回true:放行,返回false:不放行
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle ...");
        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
public class WebConfig implements WebMvcConfigurer {

    @Autowired
    public LoginCheckInterceptor loginCheckInterceptor;

    // 重写方法,注册拦截器
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginCheckInterceptor).addPathPatterns("/**");
    }
}

拦截路径:

在这里插入图片描述

执行流程:

在这里插入图片描述

Filter和Interceptor的区别:

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

6.4 异常处理

方案一:在Controller的方法中进行try…catch处理

方案二:全局异常处理器

在这里插入图片描述

@RestControllerAdvice = @ControllerAdvice + @ResponseBody

文章来源:https://blog.csdn.net/qq_44689664/article/details/135463189
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。