基于JWT的权限验证及实战演练

第三方插件 专栏收录该内容
4 篇文章 0 订阅

前言:

大部分系统,除了大部分金融类的系统需要严格的安全框架(如shiro),一般的系统安全性要求都不是很高,只需要简单的权限验证(比如登录验证)即可,下面将简单介绍JWT用法及登录验证的实现方式(注解方式)

jwt分析(原理啊什么的废话就不说了,只说用法)

  1. header(头部),头部用于描述关于该JWT的最基本的信息,例如其类型以及签名所用的算法等。(所以算法一样,头部也是一样的,下面例子可以看到)
  2. poyload(负荷),负荷基本就是自己想要存放的信息(因为信息会暴露,不应该在载荷里面加入任何敏感的数据)
  3. sign(签名),签名的作用就是为了防止恶意篡改数据
  4. 例子:eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJ3eHkifQ.l2CRkFWjXYWWUqe-_Qu4ANqx-jfxydSTN0vLGi9P51w以逗号分隔的,分为三部分
  5. 具体的jwt的解析可以去看 前后端分离之JWT用户认证这篇文章我看了,写的好不错,就其中提到的传统的seesion跟踪会话的方式做了比较,就我个人理解,下面在解释一下。

jwt登录验证和传统session验证的区别

  1. 传统的session验证存在安全问题,主要是因为session是存在cookie,具体弊端引用别人说的:

但这样做问题就很多,如果我们的页面出现了 XSS 漏洞,由于 cookie 可以被 JavaScript 读取,XSS 漏洞会导致用户 token 泄露,而作为后端识别用户的标识,cookie 的泄露意味着用户信息不再安全。尽管我们通过转义输出内容,使用 CDN 等可以尽量避免 XSS 注入,但谁也不能保证在大型的项目中不会出现这个问题。

在设置 cookie 的时候,其实你还可以设置 httpOnly 以及 secure 项。设置 httpOnly 后 cookie 将不能被
JS 读取,浏览器会自动的把它加在请求的 header 当中,设置 secure 的话,cookie 就只允许通过 HTTPS
传输。secure 选项可以过滤掉一些使用 HTTP 协议的 XSS 注入,但并不能完全阻止。

2.而jwt有什么特点呢?

  1. 后端将JWT字符串作为登录成功的返回结果返回给前端。前端可以将返回的结果保存在localStorage或
    sessionStorage上,退出登录时前端删除保存的JWT即可。
  2. 简洁(Compact):可以通过URL, POST 参数或者在 HTTP header 发送,因为数据量小,传输速度快
  3. 自包含(Self-contained):负载中包含了所有用户所需要的信息,避免了多次查询数据库
  4. 前端在每次请求时将JWT放入HTTP Header中的Authorization位。(解决XSS和XSRF问题)

cookie和localStorage又有什么区别呢?

Cookie 和 LocalStorage 比较
以上是两者对比,文章看了写的不错,具体有兴趣可以看看,下面对比一下主要区别:
在这里插入图片描述

jwt实战(登录校验)

上面说的废话好像有点多,主要是自己查阅资料后的一些总结和摘取。

引入JWT相关包

   <dependency>
			<groupId>com.auth0</groupId>
			<artifactId>java-jwt</artifactId>
			<version>3.0.2</version>
	</dependency>

生成token

String token = JWT.create().withAudience(name) .sign(Algorithm.HMAC256(passwd));//生成token
redisTemplate.opsForHash().put("tokens", name, token);//存入redis

解释:以上操作是在用户点击登陆的时候,根据用户名已经密码生成的token,并且存入redis,前端登录以后服务器端会返回token,并且将其存在localStorage中
好处:存入redis的好处是redis可以设置定时删除,这就可以实现用户隔一段时间,token失效,从新登陆,从新生成token,进一步提高系统安全性,而且redis查询数据比数据库要快的多,而且会频繁查询,所以放在redis
图示:
在这里插入图片描述

登陆验证

现在token已经生成了,并且存入了redis,剩下的就是请求api时的登录验证了,如果验证失败就返回401(没有权限提示)
每次请求时,再请求头中都会携带token的信息,如下图:
在这里插入图片描述
接下来就是后端的事情了,后端采用的是在接口上面加上注解验证:
注解的具体实现方法是:

public class AuthenticationInterceptor implements HandlerInterceptor {
    @Autowired
    private RedisTemplate<String, ?> redis;
    @Value("${hashkey.web}")
    private String key;  
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response, Object handler) throws Exception {
        // 如果不是映射到方法直接通过
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method method = handlerMethod.getMethod();

        // 判断接口是否需要登录
        LoginRequired methodAnnotation = method.getAnnotation(LoginRequired.class);
        HashRequired methodAnnotation2 = method.getAnnotation(HashRequired.class);
        CurrentUser methodAnnotation3 = method.getAnnotation(CurrentUser.class);
        // 有 @LoginRequired 注解,需要认证
        if (methodAnnotation != null) {
            // 执行认证
            String token = request.getHeader("authorization");  // 从 http 请求头中取出 token
            if (StringUtil.isEmpty(token)) {
            	response.sendError(401, "no header,try login !");
				return false;
            }
            String name=null;
			try {
				name = JWT.decode(token).getAudience().get(0);
				if (methodAnnotation3 != null) {
            		request.setAttribute("user", name);
            	}
			} catch (Exception e) {
				response.sendError(401, "wrong token!");
				return false;
			}
               // 验证token
            	if(!redis.opsForHash().hasKey("tokens", name)){
            		response.sendError(401, "no user token,try login!");
    				return false;
            	}
            	
            	String rtoken = redis.opsForHash().get("tokens", name).toString();
            	
            	if (!token.equals(rtoken)) {
            		response.sendError(401, "token invalid ,try login!");
    				return false;
				}
            return true;
        }
        if (methodAnnotation2 != null) {
            // 执行认证
        	String hashkey = request.getHeader("hashkey");
            if (StringUtil.isEmpty(hashkey)) {
            	response.sendError(401, "no key,reject !");
				return false;
            }
               // 验证hashkey
            if (!key.equals(hashkey)) {
            	response.sendError(401, "wrong key,reject !");
				return false;
			}            return true;
        }
        return true;
    }

    public void postHandle(HttpServletRequest request,
                           HttpServletResponse response, Object handler,
                           ModelAndView modelAndView) throws Exception {
    }

    public void afterCompletion(HttpServletRequest request,
                                HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
    }
}

解释:以上是一个全局请求拦截器,每次请求都会先判断这个,根据前端传来的token,将token解析,在到redis根据name取得原来登录存的token对比即可实现。

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface LoginRequired {
}

解释:以上是LoginRequired 接口的实现
总结:项目采用的是springboot+springcloud+maven,基本上全部用的注解,如有问题,欢迎提出来。

  • 2
    点赞
  • 5
    评论
  • 15
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

相关推荐
<p> <span style="font-size:16px;"><br /> </span> </p> <p> <span style="font-size:16px;"><strong>课程简介:<br /> </strong>本课程主要是跟各位小伙伴分享、介绍并实战两大核心用户身份认证(接口鉴权)模式,即</span><span style="font-size:16px;">基于</span><span style="font-size:16px;">Token</span><span style="font-size:16px;">认证模式</span><span style="font-size:16px;"> 以 </span><span style="font-size:16px;">基于</span><span style="font-size:16px;">Session</span><span style="font-size:16px;">认证模式</span><span style="font-size:16px;">,其中</span><span></span> </p> <p> <span style="font-size:16px;">(1)   </span><span style="font-size:16px;">基于</span><span style="font-size:16px;">Token</span><span style="font-size:16px;">认证模式</span><span style="font-size:16px;"> 则主要介绍了三种核心、主流认证模式,即基于</span><span style="font-size:16px;">Token+</span><span style="font-size:16px;">数据库、基于</span><span style="font-size:16px;">Token+</span><span style="font-size:16px;">缓存中间件</span><span style="font-size:16px;">Redis</span><span style="font-size:16px;">、基于</span><span style="font-size:16px;">Token+JWT</span><span style="font-size:16px;">认证模式。</span><span></span> </p> <p> <span style="font-size:16px;">(2)   </span><span style="font-size:16px;">基于</span><span style="font-size:16px;">Session</span><span style="font-size:16px;">认证模式 </span><span style="font-size:16px;">也主要介绍了三种核心、主流认证模式,即基于原生</span><span style="font-size:16px;">Spring Session</span><span style="font-size:16px;">以</span><span style="font-size:16px;">Session</span><span style="font-size:16px;">共享认证模式、基于</span><span style="font-size:16px;">Shiro Session</span><span style="font-size:16px;">认证模式、基于</span><span style="font-size:16px;">Shiro + Redis </span><span style="font-size:16px;"></span><span style="font-size:16px;">Session</span><span style="font-size:16px;">共享认证模式</span><span></span> </p> <p> <span style="font-size:16px;">即课程整体介绍如下图所示:</span> </p> <p> <span style="font-size:16px;"><img src="https://img-bss.csdn.net/201909120730297517.png" alt="" /><br /> </span> </p> <p> <span style="font-size:16px;"> </span> </p> <p> 核心技术栈列表: </p> 值得介绍是,本课程在技术栈层面涵盖了“用户身份认证”、“接口鉴权”等业务场景 常用大部分技术,包括<span>Spring Boot2.x</span>、<span>Spring MVC</span>、<span>Mybatis</span>、加密解密算法<span>AES</span>、雪花算法<span>Snowflake</span>、统一验参工具<span>ValidatorUtil</span>、<span>JWT</span>(<span>Json Web Token</span>)、缓存中间件<span>Redis</span>、<span>Shiro(</span>身份认证与会话等等<span>)</span>、过滤器<span>Filter</span>、拦截器<span>Interceptor</span>、热部署插件<span>Devtools</span>、等等,如下图所示<br /> <p> <span style="font-size:16px;"><img src="https://img-bss.csdn.net/201909120732073201.png" alt="" /><br /> </span> </p> <p> <span style="font-size:16px;"> </span> </p> <p> <br /> </p> <p> 值得一提是,本课程所介绍核心重点在于“仅仅围绕基于<span>Token</span>认证模式”进行展开讲解与实战,如下图所示为<span>Debug</span>亲自罗列、归纳出来几大核心要点(面试官就经常喜欢这样面): </p> <img src="https://img-bss.csdn.net/201909120732381227.png" alt="" /><br /> <p> <span style="font-size:16px;"><br /> </span> </p> <p> <span style="font-size:16px;"> </span> </p> <p> 如下图所示为 基于<span>Token</span>认证模式 总体上时序图:<span></span> </p> <img src="https://img-bss.csdn.net/201909120733009772.png" alt="" /><br /> <p> <br /> </p>
©️2020 CSDN 皮肤主题: 数字20 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值