springboot-登录认证

登陆:

LoginController:

@PostMapping("/login")
    public Result Login(@RequestBody Emp emp){//用emp实体类配合@RequestBody来对JSON格式的数据进行封装
        Emp e=empService.login(emp);
        return e!=null?Result.success():Result.error("用户名或密码错误");
    }

EmpService:

@Override
    public Emp login(Emp emp) {
        return empMapper.getByUsernameAndPassword(emp);
    }

EmpMapper:

@Select("select * from emp where username=#{username} and password=#{password}")
    Emp getByUsernameAndPassword(Emp emp);

-----------------------------------------------------

上述存在的问题,若直接复制网站地址,则不用登录就可以进入相应网页。

进行“统一拦截”来判断当前用户是否登录。若没有获取到登录标记/获取到的登录标记有问题,则跳转到登录页面。

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

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

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

目前,令牌技术是主流方案。

跨域:前后端部署的服务器地址(ip地址)不同

-----------------------------------------------------------------------

JWT令牌:全称:JSON  Web  Token (https://jwt.io/) 定义了一种简洁的、自包含的格式,用于在通信双方以json数据格式安全的传输信息。由于数字签名的存在,这些信息是可靠的。

场景:登录认证。 登录成功后,生成令牌 后续每个请求,都要携带JWT令牌,系统在每次请求处理之前,先校验令牌,通过后,再处理。

组成: 第一部分:Header(头), 记录令牌类型、签名算法等。 例如:{"alg":"HS256","type":"JWT"}

第二部分:Payload(有效载荷),携带一些自定义信息、默认信息等。 例如:{"id":"1","username":"Tom"}

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

JWT令牌的生成与解析:

@Test
    public void testGenJwt(){//测试JWT令牌生成
        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))//令牌有效期
                .compact();
        System.out.println(jwt);
    }

    @Test
    public void testParseJwt(){//解析Jwt令牌
        Claims claims =Jwts.parser()
                .setSigningKey("itheima")
                .parseClaimsJws("eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoidG9tIiwiaWQiOjEsImV4cCI6MTcwNjY5ODk2M30.fFlv2QoRRUPKUes6BF5Db91Mhxu4b_Hqh6jEXD9ZB8M")
                .getBody();
        System.out.println(claims);
    }

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

----------------------------------------------------

引入令牌后的返回数据:

用户登录成功后,系统会自动下发JWT令牌,然后在后续的每次请求中,都需要在请求头header中携带到服务端,请求头的名称为 token ,值为 登录时下发的JWT令牌。

LoginController:

@PostMapping("/login")
    public Result Login(@RequestBody Emp emp){//用emp实体类配合@RequestBody来对JSON格式的数据进行封装
        Emp e=empService.login(emp);
        //若登录成功,则生成令牌并下发令牌
        if(e!=null){
            Map<String,Object> claims=new HashMap<>();
            //将员工的id、姓名、用户名封装到Map集合中
            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令牌存储在浏览器本地。在请求头中包含一个token(JWT令牌),后续的每一次请求当中,都会将这个令牌携带到服务端。

----------------------------------------------

统一拦截校验令牌:过滤器、拦截器

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

Filter 可以根据需求,配置不同的拦截资源路径

一个web应用中,可以配置多个过滤器,这多个过滤器就形成了一个过滤器链。只有最后一个过滤器放行后,才会访问对应的web资源

----------------------------------------------------

登录校验过滤器:

登录校验Filter-流程:

获取请求url。

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

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

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

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

放行。

LoginCheckFilter:

@Slf4j
@WebFilter(urlPatterns = "/*")
public class LoginCheckFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest req=(HttpServletRequest) servletRequest;//强制类型转换
        HttpServletResponse resp=(HttpServletResponse) servletResponse;//强制类型转换
        //获取请求url。
        String url=req.getRequestURL().toString();
        //判断请求url中是否包含login,如果包含,说明是登录操作,放行。
        if(url.contains("login")){
            filterChain.doFilter(servletRequest,servletResponse);//放行
            return;
        }
        //获取请求头中的令牌(token)。
        String jwt=req.getHeader("token");
        //判断令牌是否存在,如果不存在,返回错误结果(未登录)。
        if(!StringUtils.hasLength(jwt)){//如果字符串没有长度,说明为null
            log.info("请求头中token为空");
            Result error=Result.error("NOT_LOGIN");
            String str=JSONObject.toJSONString(error);//手动将对象转换为JSON格式的数据
            resp.getWriter().write(str);
            return;
        }
        //解析token,如果解析失败,返回错误结果(未登录)。
        try{
            JwtUtils.parseJWT(jwt);
        } catch (Exception e){//jwt解析失败
            e.printStackTrace();
            Result error=Result.error("NOT_LOGIN");
            String str=JSONObject.toJSONString(error);//手动将对象转换为JSON格式的数据
            resp.getWriter().write(str);
            return;
        }
        //放行。
        log.info("放行");
        filterChain.doFilter(servletRequest,servletResponse);
    }
}

---------------------------------------------------

拦截器:

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

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

----------------------------------------------------------------

过滤器和拦截器的区别:

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

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

LoginCheckInterceptor:
@Slf4j
@Component
public class LoginCheckInterceptor implements HandlerInterceptor {
    @Override//目标资源方法运行前运行,返回true则放行
    public boolean preHandle(HttpServletRequest req, HttpServletResponse resp, Object handler) throws Exception {
        //获取请求url。
        String url=req.getRequestURL().toString();
        //判断请求url中是否包含login,如果包含,说明是登录操作,放行。
        if(url.contains("login")){
            return true;//放行
        }
        //获取请求头中的令牌(token)。
        String jwt=req.getHeader("token");
        //判断令牌是否存在,如果不存在,返回错误结果(未登录)。
        if(!StringUtils.hasLength(jwt)){//如果字符串没有长度,说明为null
            log.info("请求头中token为空");
            Result error=Result.error("NOT_LOGIN");
            String str= JSONObject.toJSONString(error);//手动将对象转换为JSON格式的数据
            resp.getWriter().write(str);
            return false;
        }
        //解析token,如果解析失败,返回错误结果(未登录)。
        try{
            JwtUtils.parseJWT(jwt);
        } catch (Exception e){//jwt解析失败
            e.printStackTrace();
            Result error=Result.error("NOT_LOGIN");
            String str=JSONObject.toJSONString(error);//手动将对象转换为JSON格式的数据
            resp.getWriter().write(str);
            return false;
        }
        //放行。
        log.info("放行");
        return true;

    }

    @Override//目标资源方法运行后运行
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("post");
    }

    @Override//视图渲染完毕后运行,最后运行
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("after");
    }
}

-----------------------------------------------

异常处理:

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(Exception.class)//捕获所有异常
    public Result ex(Exception ex){
        ex.printStackTrace();
        return Result.error("操作失败");
    }
}

  • 18
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值