Springboot 를 이용 하여 Jwt 인증 을 실현 하 는 예시 코드

14574 단어 SpringbootJwt인증
JSON Web Token 은 현재 가장 유행 하 는 크로스 도 메 인 인증 솔 루 션 으로 앞 뒤 분리 프로젝트 가 Restful API 를 통 해 데이터 상호작용 을 할 때 인증 하기에 적합 합 니 다.
image-20200221234116337
Shiro 통합 JWT 에 대해 서 는 여 기 를 볼 수 있 습 니 다Springboot Shiro+JWT 인증 실현
개술
개념 적 인 내용 이 인터넷 에 많 기 때문에 상세 하 게 소개 하지 않 겠 습 니 다.
구체 적 으로 는 여 기 를 볼 수 있다.
나 는 몇 가지 중점 을 총 결 했다.
JWT,전 칭 JSon Web Token,영패 인증 방식
생김새:
image-20200221230910489
머리:서명 알고리즘 과 토 큰 유형(이것 이 바로 JWT)을 넣 습 니 다4.567917.부하:당신 이 토 큰 에 첨부 한 정보:예 를 들 어 사용자 의 id,사용자 의 전화번호 등 이 있 습 니 다.그러면 나중에 토 큰 을 검증 한 후에 바로 여기 서 정 보 를 얻 을 수 있 습 니 다.데이터 베 이 스 를 찾 지 않 아 도 됩 니 다서명:영패 추가 용안전성:부하 에 있 는 내용 은 모두 BASE 64 로 처리 되 기 때문에 비밀 성 이 없다(BASE 64 는 대칭 적 이기 때문이다).그러나 서명 인증 으로 인해 다른 사람들 은 데 이 터 를 위조 하기 어렵다.그러나 이것 은 민감 한 정보,예 를 들 어 비밀 번 호 를 부하 에 넣 으 면 안 된다 는 것 을 의미한다.왜냐하면 이런 것 은 다른 사람 이 직접 볼 수 있 지만 사용자 id 와 같은 것 은 상관없다.
작업 흐름
로그 인 단계
사용자 가 처음으로 로그 인 하여 계 정 비밀 번 호 를 비교 하여 로그 인 성공 여 부 를 판단 합 니 다.로그 인 에 성공 하면 jwt 문자열 을 생 성 한 다음 에 추가 정 보 를 넣 어 클 라 이언 트 에 게 되 돌려 줍 니 다.
image-20200221234215011
이 jwt 문자열 에는 사용자 와 관련 된 정보 가 포함 되 어 있 습 니 다.예 를 들 어 이 사용자 가 누구 인지,그의 id 가 얼마 인지,이 영패 의 유효 시간 이 얼마 인지 등 입 니 다.다음 사용자 가 로그 인 할 때 이 영패 도 함께 가 져 가 야 합 니 다.
인증 단계
여 기 는 전단 과 통일 적 으로 약속 해 야 합 니 다.요청 을 할 때 지난번 token 을 요청 머리 에 있 는 한 위치 에 두 고 보 냅 니 다.백 엔 드 가 요청 을 받 은 후에 jwt 를 분석 하여 jwt 가 합 법 적 인지,위조 되 었 는 지,기한 이 지 났 는 지 검증 합 니 다.여기까지 검증 과정 이 완료 되 었 습 니 다.
image-20200221234900958
그러나 서버 역시 검 증 된 jwt 에서 사용자 의 관련 정 보 를 얻어 데이터베이스 에 대한 조 회 를 줄 일 수 있다.
예 를 들 어 우 리 는'사용자 전화 번 호 를 통 해 사용자 의 잔액 을 조회 합 니 다'라 는 업무 가 있 습 니 다.
만약 에 우리 가 jwt 의 부하 에 전화번호 라 는 속성 을 미리 두 었 다 면 우 리 는 먼저 데이터 베 이 스 를 가서 사용자 id 에 따라 사용자 의 전화 번 호 를 조회 하고 전화 번 호 를 직접 받 은 다음 에 받 는 업무 논 리 를 수행 하 는 것 을 피 할 수 있 습 니 다.
유효기간
jwt 는 사용자 에 게 직접 주 는 것 이기 때문에 성공 적 인 jwt 를 검증 할 수 있다 면 로그 인 성공 으로 볼 수 있 습 니 다.따라서 jwt 에 만 료 시간 을 설정 하지 않 으 면 사용 자 는 이 jwt 를 저장 하면 영원히 로그 인 하 는 것 과 같 습 니 다.이것 은 안전 하지 않 습 니 다.만약 에 이 번호판 이 누설 되면그러면 서버 는 이 영패 소지 자의 접근 을 막 을 방법 이 없습니다.(이 영패 를 받 으 면 마음대로 신분 을 사칭 하여 방문 하 는 것 과 같 기 때문에)jwt 는 유효기간 이 있 습 니 다.보통 부하 부분 에 존재 합 니 다.다음은 jwt 를 생 성 하 는 자바 코드 입 니 다.

 return JWT.create().withAudience(userId)
  .withIssuedAt(new Date()) <----     
  .withExpiresAt(expiresDate) <----    
  .withClaim("sessionId", sessionId)
  .withClaim("userName", userName)
  .withClaim("realName", realName)
  .sign(Algorithm.HMAC256(userId+"HelloLehr"));
실제 개발 에 서 는 영패 의 유효기간 이 짧 을 수록 안전 하 다.영패 가 자주 변 하기 때문에 어떤 영패 가 다른 사람 에 게 도용 되 더 라 도 곧 효력 을 잃 기 때문이다.그러나 유효기간 이 짧 으 면 사용자 체험 이 좋 지 않 을 수 있 기 때문에 이 럴 때 또 다른 영패 가 나타난다refresh token 영패 리 셋 의 유효기간 은 매우 길 것 입 니 다.영패 리 셋 이 기한 이 지나 지 않 았 다 면 다른 jwt 를 신청 할 수 있 습 니 다.로그 인 할 필요 가 없습니다.(그리고 이 과정 은 사용자 가 특정한 인 터 페 이 스 를 방문 할 때 자동 으로 완 성 됩 니 다.사용 자 는 영패 교체 가 느껴 지지 않 습 니 다)영패 리 셋 에 대한 구체 적 인 실 제 는 자세히 말씀 드 리 지 않 겠 습 니 다.(사실은 저도 XD 를 깊이 연구 한 적 이 없 기 때 문 입 니 다.)
대비 세 션
전통 적 인 session 세 션 메커니즘 에서 서버 식별 사용 자 는 사용자 가 처음으로 서버 에 접 근 했 을 때 사용자 에 게 sessionId 를 주 고 사용자 가 대응 하 는 세 션 기록 을 서버 에 두 었 다가 매번 sessionId 를 통 해 대응 하 는 세 션 기록 을 찾 았 다.이렇게 하면 모든 데이터 가 서버 에 존재 하 는 것 은 안전 하지만 분포 식 응용 에 있어 서 는 session 공유 문 제 를 고려 해 야 합 니 다.그렇지 않 으 면 같은 사용자 의 session Id 요청 이 다른 서버 에 자동 으로 배정 되면 효력 을 잃 는 것 과 같 습 니 다.
그리고 Jwt 는 로그 인 인증 에 사용 할 수 있 을 뿐만 아니 라 해당 하 는 데 이 터 를 사용자 에 게 되 돌려 주 었 습 니 다.서명 을 통 해 데이터 의 진실성 을 확보 합 니 다.이 응용 프로그램의 각 서버 에 통 일 된 검증 방법 이 있 습 니 다.검증 을 통과 할 수 있다 면 당신 의 번호판 이 믿 을 만하 다 는 것 을 설명 할 수 있 습 니 다.저 는 당신 의 영패 에서 당신 의 정 보 를 얻 을 수 있 습 니 다.당신 이 누구 인지 알 수 있 습 니 다.서버 의 압력 을 줄 이 고 분포 식 응용 에 도 더욱 우호 적 입 니 다.(서버 session 의 분포 식 저장 문 제 는 걱정 하지 않 아 도 되 니까)
통합 Springboot
자바-jwt 패키지 가 져 오기
가 져 오기java-jwt가방:
이 가방 에는 일련의 jwt 작업 api 가 구현 되 어 있 습 니 다.
만약 당신 이 Maven 유저 라면:
pom.xml 에 쓰기

<!-- https://mvnrepository.com/artifact/com.auth0/java-jwt -->
<dependency>
 <groupId>com.auth0</groupId>
 <artifactId>java-jwt</artifactId>
 <version>3.8.3</version>
</dependency>
만약 당신 이 Gradle 유저 라면:
build.gradle 에 쓰기

compile group: 'com.auth0', name: 'java-jwt', version: '3.8.3'
만약 당신 이 다른 유저 라면:
maven 중앙 창고 주소완 일 봉 사내 블 로그
도구 클래스 의 작성
코드 는 다음 과 같 습 니 다:

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.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;

import java.io.Serializable;
import java.util.Calendar;
import java.util.Date;

/**
 * @author Lehr
 * @create: 2020-02-04
 */
public class JwtUtils {

 /**
     :     id
     :  
     :30  
     :     :      ,      
     :    id       
 */
 public static String createToken(String userId,String realName, String userName) {

 Calendar nowTime = Calendar.getInstance();
 nowTime.add(Calendar.MINUTE,30);
 Date expiresDate = nowTime.getTime();

 return JWT.create().withAudience(userId) //    
  .withIssuedAt(new Date()) //    
  .withExpiresAt(expiresDate) //    
  .withClaim("userName", userName) //  ,        
  .withClaim("realName", realName)
  .sign(Algorithm.HMAC256(userId+"HelloLehr")); //  
 }

 /**
 *      ,  secret            id
 * @param token
 * @throws TokenUnavailable
 */
 public static void verifyToken(String token, String secret) throws TokenUnavailable {
 DecodedJWT jwt = null;
 try {
  JWTVerifier verifier = JWT.require(Algorithm.HMAC256(secret+"HelloLehr")).build();
  jwt = verifier.verify(token);
 } catch (Exception e) {
  //    
  //                 ,        
  throw new TokenUnavailable();
 }
 }

 /**
 *       
 */
 public static String getAudience(String token) throws TokenUnavailable {
 String audience = null;
 try {
  audience = JWT.decode(token).getAudience().get(0);
 } catch (JWTDecodeException j) {
  //   token    
  throw new TokenUnavailable();
 }
 return audience;
 }


 /**
 *             
 */
 public static Claim getClaimByName(String token, String name){
 return JWT.decode(token).getClaim(name);
 }
}
작은 설명:
jwt 생 성 시 암호 화 및 검증 방법:
jwt 의 검증 은 사실 jwt 의 마지막 부분(서명 부분)을 검증 하 는 것 이다.여기에 서명 한 암호 화 방식 을 지정 할 때 암호 화 된 문자열 도 들 어 왔 기 때문에 검증 할 때 암호 화 알고리즘 을 알 아야 할 뿐만 아니 라 이 문자열 을 얻어 야 복호화 에 성공 하고 안전성 을 높 일 수 있 습 니 다.제 가 사용 하 는 것 은 id 입 니 다.비교적 간단 합 니 다.만약 에 더 안전 하고 싶다 면 사용자 비밀 번 호 를 이 암호 화 문자열 로 사용 할 수 있 습 니 다.그러면 이 업무 코드 가 누설 되 더 라 도 큰 안전 문 제 를 일 으 키 지 않 을 것 입 니 다.(제 id 는 누구나 알 고 있 기 때문에 토 큰 은 위 조 될 수 있 습 니 다.그러나 비밀번호 로 바 꾸 면 데이터 베이스 가 무사 하면 아무 도 모 릅 니 다)
부하 획득 방법:
누 군 가 는 이상 하 게 생각 할 수 있 습 니 다.왜 복호화 가 필요 하지 않 고 verify 없 이 부하 에 있 는 내용 을 얻 을 수 있 습 니까?이 유 는 원래 부하 가 Base 64 로 처리 되 었 을 뿐 암호 성 이 없 기 때문에 그 값 을 직접 얻 을 수 있 습 니 다.그러나 이 값 의 진실성 을 믿 을 수 있 는 지 없 는 지 는 검증 을 통과 할 수 있 는 지 를 봐 야 합 니 다.마지막 서명 부분 은 앞 머리 와 부하 내용 과 관련 이 있 기 때문에 서명 이 검증 되면그럼 앞의 부하 가 바 뀌 지 않 았 다 는 거 야.
주해 류 의 작성
controller 층 의 모든 방법 에 있어 서 이 주 해 를 사용 하여 이 방법 에 접근 할 때 token 을 휴대 해 야 하 는 지 여 부 를 결정 할 수 있 습 니 다.기본 값 은 모든 검사 이기 때문에 일부 특수 인터페이스 에 대해 서 는 검증 면제 주 해 를 가 져 야 합 니 다.
인증 면제 주해
4.567914.인증 건 너 뛰 기,보통 입구 방법 에서 이 걸 사용 합 니 다.예 를 들 어 로그 인 인터페이스 등 입 니 다.

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author Lehr
 * @create: 2020-02-03
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface PassToken {
 boolean required() default true;
}
차단기 작성
 설정 클래스

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
* @author lehr
*/
@Configuration
public class JwtInterceptorConfig implements WebMvcConfigurer {
 @Override
 public void addInterceptors(InterceptorRegistry registry) {

 //        
 registry.addInterceptor(authenticationInterceptor())
  .addPathPatterns("/**");
 }
 @Bean
 public JwtAuthenticationInterceptor authenticationInterceptor() {
 return new JwtAuthenticationInterceptor();
 }
} 
차단기

import com.auth0.jwt.interfaces.Claim;
import com.imlehr.internship.annotation.PassToken;
import com.imlehr.internship.dto.AccountDTO;
import com.imlehr.internship.exception.NeedToLogin;
import com.imlehr.internship.exception.UserNotExist;
import com.imlehr.internship.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Map;

/**
 * @author Lehr
 * @create: 2020-02-03
 */
public class JwtAuthenticationInterceptor implements HandlerInterceptor {
 @Autowired
 AccountService accountService;

 @Override
 public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception {
 //         token            jwt        token   
 String token = httpServletRequest.getHeader("token");
 //              
 if (!(object instanceof HandlerMethod)) {
  return true;
 }
 HandlerMethod handlerMethod = (HandlerMethod) object;
 Method method = handlerMethod.getMethod();
 //     passtoken  ,      
 if (method.isAnnotationPresent(PassToken.class)) {
  PassToken passToken = method.getAnnotation(PassToken.class);
  if (passToken.required()) {
  return true;
  }
 }
 //      
 else {
  System.out.println(" jwt      ");
  //     
  if (token == null) {
  //         , token             ,        
  throw new NeedToLogin();
  }
  
  //    token    user Name
  String userId = JwtUtils.getAudience(token);

  //        user               ,          
  AccountDTO user = accountService.getByUserName(userId);

  if (user == null) {
  //           
  throw new UserNotExist();
  }

  //    token 
  JwtUtils.verifyToken(token, userId)
  
  //      
 	String userName = JwtUtils.getClaimByName(token, "userName").asString();
 	String realName = JwtUtils.getClaimByName(token, "realName").asString();
 	
  //  attribute      
  request.setAttribute("userName", userName);
 	request.setAttribute("realName", realName);
  

  return true;

 }
 return true;
 }

 @Override
 public void postHandle(HttpServletRequest httpServletRequest,
    HttpServletResponse httpServletResponse,
    Object o, ModelAndView modelAndView) throws Exception {

 }

 @Override
 public void afterCompletion(HttpServletRequest httpServletRequest,
    HttpServletResponse httpServletResponse,
    Object o, Exception e) throws Exception {
 }
}
이 코드 의 실행 논 리 는 대략 다음 과 같다.
목표 방법 에 주해 가 있 습 니까?PassToken 이 있 으 면 뒤의 인증 을 실행 하지 않 고 바로 실행 합 니 다.그렇지 않 으 면 모두 검증 이 필요 합 니 다4.567917.검증 시작:token 이 있 습 니까?없다그러면 오 류 를 되 돌려 줍 니 다.
4.567917.token 의 audience 에서 발급 대상 을 얻 고 이 사용자 가 있 는 지 확인 합 니 다(클 라 이언 트 가 조작 할 수 있 고 이 사용자 의 계 정 이 동결 되 었 을 수도 있 습 니 다).사용자 의 논 리 를 보면 Service 방법 을 직접 비교 하면 됩 니 다
  • Jwt 의 유효성 을 검증 하고 무효 또는 기한 이 지나 면 오 류 를 되 돌려 줍 니 다
  • Jwt 유효성 검사 성공:Jwt 의 부하 내용 을 얻 으 면 다음 contrller 층 에서 직접 사용 할 수 있 습 니 다(구체 적 인 사용 방법 은 뒤의 코드 를 볼 수 있 습 니 다)
  • 인터페이스의 작성
    여기 서 두 개의 인 터 페 이 스 를 설계 했다.로그 인 과 이름 조회 로 하나의 미니 업 무 를 모 의 했다.그 중에서 후 자 는 로그 인 한 후에 야 사용 할 수 있 고 대체적으로 다음 과 같다.
    image-20200222005538848
    로그 인 코드
    
    /**
     *     :         ,       ,            
     *     jwt     
     *
     * @return
     * @throws LoginFailed   LoginFailed       
     */
     @PassToken
     @GetMapping(value = "/login")
     public AccountVO login(String userName, String password) throws LoginFailed{
     
     try{
      service.login(userName,password);
     }
     catch (AuthenticationException e)
     {
      throw new LoginFailed();
     }
    
     //     ,         
     AccountVO account = accountService.getAccountByUserName(userName);
     
     //     token     
     String jwtToken = JwtUtils.createToken(account);
    
     //        token  accountVO   
     account.setToken(jwtToken);
    
     return account;
    
     }
    비 즈 니스 코드
    로그 인 이 필요 한 인 터 페 이 스 를 열거 합 니 다.
    
     @GetMapping(value = "/username")
     public String checkName(HttpServletRequest req) {
     //                        
     String name = (String) req.getAttribute("userName");
     return name;
     }
    Springboot 를 이용 하여 Jwt 인증 을 실현 하 는 예제 코드 에 관 한 이 글 은 여기까지 소개 되 었 습 니 다.더 많은 Springboot Jwt 인증 내용 은 우리 의 이전 글 을 검색 하거나 아래 의 관련 글 을 계속 조회 하 시기 바 랍 니 다.앞으로 많은 응원 바 랍 니 다!

    좋은 웹페이지 즐겨찾기