SpringBoot使用Jwt处理跨域认证问题的教程详解
在前后端开发时为什么需要用户认证呢?原因是由于HTTP协定是不存储状态的,这意味着当我们透过账号密码验证一个使用者时,当下一个request请求时他就把刚刚的资料忘记了。于是我们的程序就不知道谁是谁了。所以为了保证系统的安全,就需要验证用户是否处于登陆状态。
一、JWT的组成
JWT由Header、Payload、Signature三部分组成,分别用.分隔。
下面就是一个jwt真实的样子,说白了就是一个字符串,但是里面却存储了很重要的信息。
eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJyYXljaGVuIiwiaWQiOjIsIm5hbWUiOiJyYXkiLCJwYXNzd29yZCI6IjMyMSIsImlhdCI6MTU5MDI5OTU0OCwiZXhwIjoxNTkwMzg1OTQ4fQ.ORJNldDIfffg7D3_xu0_dBWb16y4fPLtw_r6qgScFpQ
Header:
第一部分是请求头由两部分组成,alg与typ,第一个指定的是算法,第二指定的是类型。
Payload
第二部分是主体信息组成,用来存储JWT基本信息,或者是我们的信息。
Signature
第三部分主要是给第一部分跟第二部进行签名使用的,用来验证是否是我们服务器发起的Token,secret是我们的密钥。
二、在springboot项目中使用jwt做验证
具体流程:
- 把用户的用户名和密码发到后端
- 后端进行校验,校验成功会生成token,把token发送给客户端
- 客户端自己保存token,再次请求就要在Http协议的请求头中带着token去访问服务端,和在服务端保存的token信息进行比对校验。
1.先引入jar包
io.jsonwebtoken jjwt 0.9.1
2.使用工具类生成token和验证token(生成token方法中存入了用户的信息)
publicclassJwtUtils{
//发行者
publicstaticfinalStringSUBJECT="raychen";
//过期时间一天
publicstaticfinallongEXPIRE=1000*60*60*24;
//密钥
publicstaticfinalStringAPPSECRET="raychen11";
/**
*生成jwt
*@paramuser
*@return
*/
publicstaticStringgeneJsonWebToken(Useruser){
if(user==null||user.getId()==null||user.getName()==null||user.getPassword()==null){
returnnull;
}
Stringtoken=Jwts.builder().setSubject(SUBJECT)
.claim("id",user.getId())
.claim("name",user.getName())
.claim("password",user.getPassword())
.setIssuedAt(newDate())
.setExpiration(newDate(System.currentTimeMillis()+EXPIRE))
.signWith(SignatureAlgorithm.HS256,APPSECRET).compact();
returntoken;
}
/**
*校验token
*@paramtoken
*@return
*/
publicstaticClaimscheckJWT(Stringtoken){
//仿造的token或者已过期就会报错
try{
finalClaimsclaims=Jwts.parser().setSigningKey(APPSECRET).parseClaimsJws(token).getBody();
returnclaims;
}catch(Exceptione){
System.out.println("catch...");
}
returnnull;
}
}
3.自定义注解(进行token验证)
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public@interfaceCheckToken{
booleanrequired()defaulttrue;
}
4.编写config,将后台所有请求先去拦截器(拦截器返回了true,用户才可以请求到接口)
@Configuration
publicclassInterceptorConfigimplementsWebMvcConfigurer{
@Override
publicvoidaddInterceptors(InterceptorRegistryregistry){
registry.addInterceptor(authenticationInterceptor())
.addPathPatterns("/**");//拦截所有请求,通过判断是否有@LoginRequired注解决定是否需要登录
}
@Bean
publicAuthenticationInterceptorauthenticationInterceptor(){
returnnewAuthenticationInterceptor();
}
}
5.定义拦截器(对需要token验证的请求,进行验证,验证成功返回true,失败返回false无法请求到接口)
publicclassAuthenticationInterceptorimplementsHandlerInterceptor{
publicstaticfinalGsongson=newGson();
/**
*进入controller之前进行拦截
*@paramrequest
*@paramresponse
*@paramobject
*@return
*/
@Override
publicbooleanpreHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objectobject)throwsException{
//如果不是映射到方法直接通过
if(!(objectinstanceofHandlerMethod)){
returntrue;
}
HandlerMethodhandlerMethod=(HandlerMethod)object;
Methodmethod=handlerMethod.getMethod();
//检查是否有LoginToken注释,有则跳过认证
if(method.isAnnotationPresent(LoginToken.class)){
LoginTokenloginToken=method.getAnnotation(LoginToken.class);
if(loginToken.required()){
returntrue;
}
}
//前面是不需要token验证的从http请求头中取出token
Stringtoken=request.getHeader("token");
//检查有没有需要用户权限的注解
if(method.isAnnotationPresent(CheckToken.class)){
CheckTokencheckToken=method.getAnnotation(CheckToken.class);
if(checkToken.required()){
if(token!=null){
Claimsclaims=JwtUtils.checkJWT(token);
if(null==claims){
sendJsonMessage(response,JsonData.buildError("token有误"));
returnfalse;
}
IntegeruserId=(Integer)claims.get("id");
Stringname=(String)claims.get("name");
request.setAttribute("userId",userId);
request.setAttribute("name",name);
returntrue;
}
//token为null的话返回一段json给前端
sendJsonMessage(response,JsonData.buildError("请登录"));
returnfalse;
}
}
//没有使用注解的方法直接返回true
returntrue;
}
/**
*响应数据给前端
*@paramresponse
*@paramobj
*@throwsIOException
*/
publicstaticvoidsendJsonMessage(HttpServletResponseresponse,Objectobj)throwsIOException{
response.setContentType("application/json;charset=utf-8");
PrintWriterwriter=response.getWriter();
writer.print(gson.toJson(obj));
writer.close();
response.flushBuffer();
}
}
用户登录成功后,使用工具类生成token。token在服务端不做存储,直接将token返回给客户端,客户端下次请求服务端时,使用工具类来验证header里的token是否合法。
总结
到此这篇关于SpringBoot使用Jwt处理跨域认证问题的文章就介绍到这了,更多相关SpringBoot处理跨域认证内容请搜索毛票票以前的文章或继续浏览下面的相关文章希望大家以后多多支持毛票票!