登录功能(cookie,session,jwt)

登录功能

思路分析

登录服务端的核心逻辑就是:接收前端请求传递的用户名和密码 ,然后再根据用户名和密码查询用户信息,如果用户信息存在,则说明用户输入的用户名和密码正确。如果查询到的用户不存在,则说明用户输入的用户名和密码错误。

代码实现

LoginController

@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

public interface EmpService {
​
    /**
     * 用户登录
     * @param emp
     * @return
     */
    public Emp login(Emp emp);
​
    //省略其他代码...
}

EmpServiceImpl

@Slf4j
@Service
public class EmpServiceImpl implements EmpService {
    @Autowired
    private EmpMapper empMapper;
​
    @Override
    public Emp login(Emp emp) {
        //调用dao层功能:登录
        Emp loginEmp = empMapper.getByUsernameAndPassword(emp);
​
        //返回查询结果给Controller
        return loginEmp;
    }   
    
    //省略其他代码...
}

EmpMapper

@Mapper
public interface EmpMapper {
​
    @Select("select id, username, password, name, gender, image, job, entrydate, dept_id, create_time, update_time " +
            "from emp " +
            "where username=#{username} and password =#{password}")
    public Emp getByUsernameAndPassword(Emp emp);
    
    //省略其他代码...
}

登录校验

为什么需要登录校验

我们已经完成了基础登录功能的开发与测试 但是我们发现没有登录仍然可以进入到其他页面。

而真正的登录功能应该是:登陆后才能访问其他页面,没登陆则跳转登陆页面进行登陆

思路分析

那么我们怎么实现呢?

  • 可以使用会话技术+拦截器来实现这个功能
  • 会话技术将用户登录信息保存方便后续判断用户是否登录
  • 而拦截器对所有除了登录页面之外的页面进行拦截,然后通过你使用的会话技术来判断用户是否登录,实现了登陆后才能访问其他页面(放行),没登陆则跳转登陆页面进行登陆(拦截)

三种会话技术

cookie(不常用)

设置的cookie,通过响应头Set-Cookie响应给浏览器,并且浏览器会将Cookie,存储在浏览器端。

  • 缺点:

    • 移动端APP(Android、IOS)中无法使用Cookie

    • 不安全,用户可以自己禁用Cookie

    • Cookie不能跨域

      • 现在的项目,大部分都是前后端分离的,前后端最终也会分开部署,前端部署在服务器 192.168.150.200 上,端口 80,后端部署在 192.168.150.100上,端口 8080,这个时候这个Cookie是不能使用的,因为Cookie无法跨域

session(传统方案)

介绍

Session,它是服务器端会话跟踪技术,所以它是存储在服务器端的。而 Session 的底层其实就是基于我们刚才所介绍的 Cookie 来实现的。

session的存储

服务器端在给浏览器响应数据的时候,它会将 Session 的 ID 通过 Cookie 响应给浏览器。其实在响应头当中增加了一个 Set-Cookie 响应头。这个 Set-Cookie的名字是固定的 JSESSIONID 代表的服务器端会话对象 Session 的 ID。浏览器会自动识别这个响应头,然后自动将Cookie存储在浏览器本地。

代码实现

<!--hutool-用来将对象转json-->
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.7.17</version>
</dependency>
WebConfig:
@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Autowired
    private LoginCheckInterceptor loginCheckInterceptor;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //添加loginCheckInterceptor拦截器,并且指定拦截所有路径除了login请求
        registry.addInterceptor(loginCheckInterceptor)
                .addPathPatterns("/**")
                .excludePathPatterns("/login");
    }
}
LoginCheckInterceptor:
@Slf4j
@Component
public class LoginCheckInterceptor implements HandlerInterceptor {



//    使用session会话技术
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        Emp emp =  (Emp)request.getAttribute("emp");//获取session中的用户数据
        //判断session中用户是否存在

        if(emp == null){//如果没登陆就进行拦截
            //向前端响应错误信息
            //设置响应头(告知浏览器:响应的数据类型为json、响应的数据编码表为utf-8)
            response.setContentType("application/json;charset=utf-8");
            //封装Result返回给前端,统一错误信息
            Result result = Result.error("NOT_LOGIN");
                   String json = JSONUtil.toJsonStr(result);
            response.getWriter().write(json);
            return false;
        }

        return true;
    }



}
 LoginController:
@Slf4j
@RestController
public class LoginController {

    @Autowired
    private EmpService empService;
     

    //session会话技术
    @PostMapping("/login")
    public Result login(@RequestBody Emp emp, HttpSession session){

        Emp e = empService.login(emp);
        //用户登录校验
        if(e == null){//账号密码错误,原因:数据库查不到数据返回null
            return Result.error("用户名或密码错误");
        }

        //账号密码都正确
        //保存session方便后续别的业务校验用户是否登录过
        session.setAttribute("emp",e);

        return Result.success();
    }
}

优缺点

  • 优点:Session是存储在服务端的,安全

  • 缺点:

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

      • 比如你有三个集群1,2,3,前端发请求进行论巡,先集群1然后保存用户信息,然后再发请求集群2,这个时候因为cookie和session不能在集群下使用做不到俩个集群直接的cookie和session共享

    • 移动端APP(Android、IOS)中无法使用Cookie

    • 用户可以自己禁用Cookie

    • Cookie不能跨域

解决这些问题可以使用jwt令牌

jwt(推荐)

这里的数据存储是存储在浏览器的localStorage中

  • 优点:

    • 支持PC端、移动端

    • 解决集群环境下的认证问题

    • 减轻服务器的存储压力(无需在服务器端存储)

介绍

JWT的组成: (JWT令牌由三个部分组成,三个部分之间使用英文的点来分割)

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

    • 例如:{"alg":"HS256","type":"JWT"}

  • 第二部分:Payload(有效载荷),携带一些自定义信息、默认信息等。

    • 例如:{"id":"1","username":"Tom"}

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

    签名的目的就是为了防jwt令牌被篡改,而正是因为jwt令牌最后一个部分数字签名的存在,所以整个jwt 令牌是非常安全可靠的。一旦jwt令牌当中任何一个部分、任何一个字符被篡改了,整个令牌在校验的时候都会失败,所以它是非常安全可靠的。

 思路分析
  1. 在浏览器发起请求来执行登录操作,此时会访问登录的接口,如果登录成功之后,我们需要生成一个jwt令牌,将生成的 jwt令牌返回给前端。

  2. 前端拿到jwt令牌之后,会将jwt令牌存储起来。在后续的每一次请求中都会将jwt令牌携带到服务端。

  3. 服务端统一拦截请求之后,先来判断一下这次请求有没有把令牌带过来,如果没有带过来,直接拒绝访问,如果带过来了,还要校验一下令牌是否是有效。如果有效,就直接放行进行请求的处理。

具体实现
<!-- JWT依赖-->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>


<!--hutool-用来将对象和json之间进行转换-->
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.7.17</version>
</dependency>

WebConfig:
@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Autowired
    private LoginCheckInterceptor loginCheckInterceptor;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //添加loginCheckInterceptor拦截器,并且指定拦截所有路径除了login请求
        registry.addInterceptor(loginCheckInterceptor)
                .addPathPatterns("/**")
                .excludePathPatterns("/login");
    }
}
LoginCheckInterceptor:
@Slf4j
@Component
public class LoginCheckInterceptor implements HandlerInterceptor {


 
    //使用jwt令牌技术
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//        获取请求头中的令牌(token)
        String token = request.getHeader("token");
        //判断token是否存在
        if(!StringUtils.hasLength(token)){//token不存在
            log.info("token不存在");
            //向前端响应错误信息
            //设置响应头(告知浏览器:响应的数据类型为json、响应的数据编码表为utf-8)
            response.setContentType("application/json;charset=utf-8");
            //封装Result返回给前端,统一错误信息
            Result result = Result.error("NOT_LOGIN");
            String json = JSONUtil.toJsonStr(result);
            response.getWriter().write(json);
            return false;
        }


        try {
            //解析jwt是会失败的,失败汇报出异常,这里进行处理
            JwtUtils.parseJWT(token);
        } catch (Exception e) {
            log.info("令牌解析失败!");
            //向前端响应错误信息
            //设置响应头(告知浏览器:响应的数据类型为json、响应的数据编码表为utf-8)
            response.setContentType("application/json;charset=utf-8");
            //封装Result返回给前端,统一错误信息
            Result result = Result.error("NOT_LOGIN");
            String json = JSONUtil.toJsonStr(result);
            response.getWriter().write(json);
            return false;
        }


        return true;
    }


}
LoginController:
@Slf4j
@RestController
public class LoginController {

    @Autowired
    private EmpService empService;
     

    //jwt令牌实现
    @PostMapping("/login")
    public Result login(@RequestBody Emp emp){

        Emp e = empService.login(emp);
        //用户登录校验
        if(e == null){//账号密码错误,原因:数据库查不到数据返回null
            return Result.error("用户名或密码错误");
        }

        //账号密码都正确
        //构建jwt令牌
        Map<String, Object> claims = BeanUtil.beanToMap(e);
        String jwt = JwtUtils.generateJwt(claims);
        log.info("登录生成的token令牌为:"+jwt);
        return Result.success(jwt);
    }
}

  • 13
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值