项目总结67:Springboot中使用JWT+Token鉴权示例

 START

代码示例

  pom文件

        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.7.0</version>
        </dependency>
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.4.0</version>
        </dependency>

 

  JwtUtils类:创建JWT,解析JWT

import io.jsonwebtoken.*;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
import java.util.Date;

public class JwtUtils {

    public final static String JWT_KEY = "aaabbcccdd";


    //1-创建JWT
    public static String createJWT(String id, String subject, Long ttlMillis){
        Date now = new Date();
        Date expireDate = new Date(now.getTime()+ttlMillis);
        SecretKey secretKey = generateKey();
        JwtBuilder jwtBuilder = Jwts.builder()
                .setHeaderParam("typ", "JWT")//
                .setId(id)
                .setSubject(subject)
                .setIssuer("tangyujie")
                .setIssuedAt(now)
                .setExpiration(expireDate)
                .signWith(SignatureAlgorithm.HS256, secretKey);

        //jwtBuilder.compact() = base64UrlEncodedHeader + '.' + base64UrlEncodedBody + '.' + base64UrlSignature;
        return jwtBuilder.compact();
    }


    //生成key
    private static SecretKey generateKey(){
        byte[] encodeKey = Base64.getDecoder().decode(JWT_KEY);
        return new SecretKeySpec(encodeKey,0,encodeKey.length,"AES");
    }

    //获取token中注册信息
    public static Claims getTokenClaim(String token){
        SecretKey secretKey = generateKey();
        try {
            return Jwts.parser()
                    .setSigningKey(secretKey)
                    .parseClaimsJws(token)
                    .getBody();
        } catch (Exception e) {
            return null;
        }
    }

    //验证token是否过期
    public static boolean isTokenExpired(Date expirationTime){
        return expirationTime.before(new Date());
    }
}

 

   WebAppConfig: 实现WebMvcConfigurer接口,作拦截请求

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

//WebMvcConfigurer是一个接口,提供很多自定义的拦截器,例如跨域设置、类型转化器等等
@Configuration
public class WebAppConfig implements WebMvcConfigurer {


    @Autowired
    private TokenInterceptor tokenInterceptor;

    //静态资源处理(这里是开放了Swagger组件的鉴权,因项目而异)
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("swagger-ui.html")
                .addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("doc.html")
                .addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**")
                .addResourceLocations("classpath:/META-INF/resources/webjars/");
        registry.addResourceHandler("/swagger-resources/**")
                .addResourceLocations("classpath:/META-INF/resources/swagger-resources/**");
        registry.addResourceHandler("/swagger/**")
                .addResourceLocations("classpath:/META-INF/resources/swagger*");
        registry.addResourceHandler("/v2/**")
                .addResourceLocations("classpath:/META-INF/resources/v2/**");
    }


    //拦截器:拦截或允许请求
    @Override
    public  void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(tokenInterceptor)
                .addPathPatterns("/**")
                .excludePathPatterns("/login")
                .excludePathPatterns("/doc.html","/swagger-resources/**","/webjars/**","/v2/**","/swagger-ui.html","/","/error","/csrf","/favico*","/api-docs","swagger.json");
    }
}

 

  TokenInterceptor类:拦截器

import io.jsonwebtoken.Claims;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.security.SignatureException;

@Component
public class TokenInterceptor extends HandlerInterceptorAdapter {

    private final Logger logger = LoggerFactory.getLogger(TokenInterceptor.class);



    //在方法被调用前执行。
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        logger.info("request start urL:{}",request.getRequestURL());
        String token = request.getHeader("Authorization");//从header中获取Authorization
        //重新登陆判断
        if(StringUtils.isEmpty(token)){
            throw new SignatureException("登陆信息为空,或者失效,请重新登陆");
        }
        Claims claims = JwtUtils.getTokenClaim(token);//从token中解析出Claims
        if(claims == null){
            throw new SignatureException("登陆信息为空,或者失效,请重新登陆");
        }
        if(JwtUtils.isTokenExpired(claims.getExpiration())){
            throw new SignatureException("登陆信息过期,请重新登陆");
        }
        String account = claims.getSubject();//从claims中获取subject
        request.setAttribute("account",account);//将数据放在request中
        return true;
    }

    //在方法执行后调用
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
                           @Nullable ModelAndView modelAndView) throws Exception {
        logger.info("request over  uri:{}",request.getRequestURI());
    }


}

  controller类:模拟接口请求

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.UUID;

/***
*@description
*@author viruser
*@date 2020/9/8 20:04
*/
@RestController
@RequestMapping("")
public class LoginController {

    //1-登陆(认证+生成token)
    @GetMapping("/login")
    public Object login(
            @RequestParam String account,
            @RequestParam String password,
            HttpServletResponse response){
        //1- 根据帐号密码请求数据库,获取用户信息
        //......模拟用户,将account放进accessToken中
        //2- 生成jwt
        String accessToken = JwtUtils.createJWT(UUID.randomUUID().toString(), account, 7200000L);
        //3-在header中返回accessToken
        response.setHeader("accessToken",accessToken);
        return "success and accessToken: " + accessToken;
    }


    //2-查询(示例如何获取请求域中的数据)
    @GetMapping("/user")
    public String getUserInfo(HttpServletRequest request){
        String account = String.valueOf(request.getAttribute("account"));////从request中取出数据
        return "account: " + account;
    }


}

 

 

 

 

 

 

END

posted on 2020-09-09 20:33  我不吃番茄  阅读(680)  评论(0编辑  收藏  举报