• Java实现登录功能(一)


    Java实现登录功能(一)


    文章目录


    前言

    刚开始学习Java的时候,你是否有过疑问,登录功能就是简单地查询数据库对比一下账号密码就行了吗?在进行权限认证的时候,又该怎么办呢?每次都在提交的表单上加上账号密码吗?在这种情况下又该怎么防止CSRF(跨站请求伪造)呢
    以下介绍一种现在流行的基于JWT的Token认证实现的登录功能。


    一、Token的认证流程及优点

    认证流程:

    1.客户端发送带有用户名和密码参数的登录请求
    2.服务端收到请求,验证用户名和密码
    3.验证通过后,服务端签发Token并返回token给客户端
    4.客户端收到Token后存储到Cookie或者Storage等中
    5.客户端每次向服务端请求资源时需要在cookie或者header请求头中携带服务端签发的token
    6.服务端收到请求后验证客户端请求携带的token,如果验证通过就向客户端返回请求数据
    在这里插入图片描述

    一般情况下,Token是被Header请求头所携带的。
    有着以下优点:

    1.支持跨域:Cookie是无法跨域的,而Token可以放在header请求头发送,所以支持跨域。
    2.无状态:Token认证方式在服务端不需要像传统的Session认证方式那样去记录SessionId,因为Token本身包含了登录用户的信息,可以减轻服务端的压力。
    3.支持移动端、小程序:当客户端是非浏览器平台时,Cookie是不被支持的,此时采用在Header请求头携带Token的认证方式会显得简单
    4.能避免CSRF失效:由于CSRF依赖于Cookie,所以由Header请求头携带Token的认证方式不会发生CSRF,所以能避免CSRF

    说明

    CSRF——跨站请求伪造。其原理是通过伪造成其它内容的请求地址,诱导用户触发请求。如果用户在登录状态下触发请求,将会自动携带Cookie访问服务端,服务端接收请求后检查到Cookie内部的用户信息会认为是登录用户本人的操作。

    二、JWT

    JWT(JSON Web Token)是一种比较主流的使用规范。其结构分别由Header标头、Payload有效负载、Signature签名算法三部分组成。

    1.Header负责记录令牌类型、签名算法等。
    such as: {"alg":"HS256","type","JWT"}
    
    2.Payload负责声明一些标准以及携带一些用户信息
    {"userId":"1","username":"mayikt"}
    标准声明:
    	iss:JWT签发者
    	sub:JWT所面向的用户
    	aud:JWT的接收者
    	exp:JWT的过期时间,过期时间必须大于签发时间
    	nbf:JWT的生效时间
    	iat:JWT的签发时间
    	jti:JWT的唯一身份标识,避免重复
    自定义的声明:
    	一些不敏感的用户信息。
    such as: {"iss":"CJJ","sub":"custom","exp":"1640000000","iat":"1632000000","jti":"156sad54a615a4d56ad15a6sd15s8924","userId":"1"}
    
    3.Signature防止Token被篡改,加盐,确保安全性
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    三、Java基于JWT实现登录功能

    1.引入pom依赖

            
                com.auth0
                java-jwt
                3.18.3
            
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2.编写JWT工具类

    import com.auth0.jwt.JWT;
    import com.auth0.jwt.JWTVerifier;
    import com.auth0.jwt.algorithms.Algorithm;
    import com.auth0.jwt.exceptions.JWTDecodeException;
    import com.auth0.jwt.interfaces.DecodedJWT;
    import com.example.demo.domain.User;
    
    import java.io.UnsupportedEncodingException;
    import java.util.Date;
    import java.util.HashMap;
    import java.util.Map;
    
    public class JWTUtil {
        //token有效时长
        private static final long EXPIRE=30*60*1000L;
        //token的密钥 可自行定义
        private static final String SECRET="jwt";
    
    
        public static String createToken(User user) throws UnsupportedEncodingException {
            //token过期时间
            Date date=new Date(System.currentTimeMillis()+EXPIRE);
    
            //jwt的header部分
            Mapmap=new HashMap<>();
            map.put("alg","HS256");
            map.put("typ","JWT");
    
            //使用jwt的api生成token
            String token= JWT.create()
                    .withHeader(map)
                    .withClaim("username", user.getUsername())//私有声明
                    .withExpiresAt(date)//过期时间
                    .withIssuedAt(new Date())//签发时间
                    .sign(Algorithm.HMAC256(SECRET));//签名
            return token;
        }
    
        /**
         * 校验token的有效性
         * 1 token的header和payload是否没改过
         * 2 没有过期
         */
        public static boolean verify(String token){
            try {
                //解密
                JWTVerifier verifier=JWT.require(Algorithm.HMAC256(SECRET)).build();
                verifier.verify(token);
                return true;
            }catch (Exception e){
                return false;
            }
        }
        //无需解密也可以获取token的信息
        public static String getUsername(String token){
            try {
                DecodedJWT jwt = JWT.decode(token);
                return jwt.getClaim("username").asString();
            } catch (JWTDecodeException e) {
                return null;
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63

    3.编写登录接口

    domain、mapper、service层请自行编写,其中Result类是统一返回结果集类

    import com.example.demo.domain.User;
    import com.example.demo.service.UserService;
    import com.example.demo.utils.JWTUtil;
    import com.example.demo.utils.Result;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.*;
    
    import java.io.UnsupportedEncodingException;
    
    @RestController
    public class LoginController {
    
        @Autowired
        private UserService userService;
    
        @PostMapping("/login")
        public Result login(@RequestParam String username, @RequestParam String password) throws UnsupportedEncodingException {
            User user=userService.getUserByPass(username, password);
            if(user == null){
            	return Result.fail("用户名或密码错误");
            }
            //登录成功则调用JWTUtil类的创建Token方法返回客户端
            String token= JWTUtil.createToken(user);
            return Result.succ(200,"登陆成功",token);
        }
        
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    4.Java后端校验Token测试

    import com.example.demo.utils.Result;
    import com.example.demo.utils.JWTUtil;
    import javax.servlet.http.HttpServletRequest;
    import org.springframework.web.bind.annotation.*;
    
    @RestController
    @RequestMapping("/user")
    public class UserController {
    
        @GetMapping("/test")
        public Result test(HttpServletRequest request){
        	String token=req.getHeader("Authorization");
        	if(token == null){
        		return Result.fail("尚未登录");
        	}
        	//获取到token中的用户信息
        	System.out.println(JWTUtil.getUsername(token));
        	//可自行编写获取用户信息后的操作
        	//......
        	
            return Result.succ("test");
        }
        
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    此处仅进行测试,后续博客将介绍安全框架搭配使用。

    5.Vue登录页面中的存储Token方法

    methods: {
        submitLogin() {
          this.$refs.loginForm.validate((valid) => {
            if (valid) {
              this.loading = true
              this.postRequest('/login', this.loginForm, 'form').then((resp) => {
                if (resp) {
                  // 存储用户token
                  const tokenStr = resp.obj.tokenHead + resp.obj.token
                  window.sessionStorage.setItem('tokenStr', tokenStr)
    
                  let path = this.$route.query.redirect
                  this.$router.replace(
                    path == '/' || path == undefined ? '/home' : path
                  )
                }
              })
            } else {
              this.$message.error('你还有未填写的信息')
              return false
            }
          })
        },
      },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    6.Vue前端每次请求资源携带Token方法

    import axios from 'axios'
    // 请求拦截器
    axios.interceptors.request.use(config => {
            if (window.sessionStorage.getItem('tokenStr')) {
                //请求携带自定义token
                config.headers['Authorization'] =
                    window.sessionStorage.getItem('tokenStr');
            }
            return config
        }, error => {
            console.log(error);
        })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    温馨提示:
    遇到报错跨域问题可参考 解决跨域问题


    总结

    本文简单地介绍了基于JWT的Token认证方式实现的登录功能。

    先自我介绍一下,小编13年上师交大毕业,曾经在小公司待过,去过华为OPPO等大厂,18年进入阿里,直到现在。深知大多数初中级java工程师,想要升技能,往往是需要自己摸索成长或是报班学习,但对于培训机构动则近万元的学费,着实压力不小。自己不成体系的自学效率很低又漫长,而且容易碰到天花板技术停止不前。因此我收集了一份《java开发全套学习资料》送给大家,初衷也很简单,就是希望帮助到想自学又不知道该从何学起的朋友,同时减轻大家的负担。添加下方名片,即可获取全套学习资料哦

  • 相关阅读:
    openpyxl关于区域的操作
    域名放Cloudflare託管後,email發不出去怎麼辦
    通过Jmeter压测存储过程
    文件包含漏洞
    ET框架6.0分析三、网络通信
    【AI应用探讨】— Gemma2模型应用场景
    BurpSuite的Cluster Bomb模式下使用Runtime File爆破不成功解决办法——路径全英文
    深入理解联邦学习——纵向联邦学习
    模型部署笔记--Pytorch-FX量化
    Cassandra 安装部署
  • 原文地址:https://blog.csdn.net/segegefe/article/details/126106809