Spring Security 코드 는 JWT 인터페이스 권한 부여 와 검증 기능 을 실현 합 니 다.

11734 단어 SpringSecurityjwt
필자 의 앞의 두 글 의 설명 을 통 해 JWT 가 무엇 인지,어떻게 사용 하 는 지,Spring Security 와 어떻게 결합 하여 사용 해 야 하 는 지 이미 알 고 있 을 것 이 라 고 믿는다.그러면 이 절 은 코드 로 JWT 로그 인 인증 및 인증 절 차 를 구체 적 으로 실현 합 니 다.
1.환경 준비 작업
  • Spring Boot 프로젝트 를 구축 하고 Spring Security 를 통합 하여 프로젝트 를 정상적으로 시작 할 수 있 습 니 다
  • controller 를 통 해 HTTP GET 방법 서비스 인 터 페 이 스 를 작성 합 니 다.예 를 들 어"/hello"
  • 은 가장 기본 적 인 동적 데이터 검증 과 권한 분 배 를 실현 한다.즉,UserDetails Service 인터페이스 와 UserDetails 인 터 페 이 스 를 실현 한다.이 두 인 터 페 이 스 는 모두 Spring Security 에 사용자,역할,권한 등 검증 정 보 를 제공 하 는 인터페이스
  • 이다.
  • Spring Security 의 formLogin 로그 인 모드 를 배 웠 다 면 HttpSecurity 설정 의 formLogin()설정 을 모두 제거 하 십시오.JWT 는 JSON 인 터 페 이 스 를 완전히 사용 하기 때문에 from 폼 이 제출 되 지 않 았 다.
  • HttpSecurity 설정 에 csrf().disable(),즉 크로스 오 버 공격 CSRF 의 방 어 를 잠시 꺼 야 합 니 다.이렇게 하 는 것 은 안전 하지 않 으 니,우 리 는 후속 장 에서 다시 처리 하 자.
  • 이상 의 내용 은 우리 가 이전의 문장 에서 이미 말 한 적 이 있다.만약 여전히 익숙 하지 않다 면,본 호 이전의 문장 을 뒤 져 볼 수 있다.
    \#\#2.JWT 도구 류 개발
    Maven 좌 표를 통 해 JWT 공구 꾸러미 jjwt 도입
    
    <dependency>
     <groupId>io.jsonwebtoken</groupId>
     <artifactId>jjwt</artifactId>
     <version>0.9.0</version>
    </dependency>
    application.yml 에 JWT 에 대한 사용자 정의 설정 을 추가 합 니 다.
    
    jwt: 
     header: JWTHeaderName
     secret: aabbccdd 
     expiration: 3600000 
  • 중 header 는 JWT 토 큰 을 가 진 HTTP 의 Header 이름 입 니 다.저 는 JWTHeaderName 이 라 고 부 르 지만 실제 생산 에 서 는 가 독성 이 떨 어 질 수록 안전 합 니 다.
  • secret 는 JWT 의 기본 정 보 를 암호 화하 고 복호화 하 는 키 입 니 다.비록 나 는 여기에서 프로필 에 죽 었 지만,실제 생산 에 서 는 프로필 에 직접 쓰 지 않 는 다.응용 프로그램의 시작 매개 변 수 를 통 해 전달 되 고 정기 적 으로 수정 해 야 합 니 다.
  • expiration 은 JWT 영패 의 유효 시간 이다.
  • Spring Boot 설정 에서 자동 으로 불 러 오 는 도구 클래스 를 작성 합 니 다.
    
    @Data
    @ConfigurationProperties(prefix = "jwt") //      ,prefix      
    @Component
    public class JwtTokenUtil implements Serializable {
     private String secret;
     private Long expiration;
     private String header;
     /**
     *   token  
     *
     * @param userDetails   
     * @return  token 
     */
     public String generateToken(UserDetails userDetails) {
     Map<String, Object> claims = new HashMap<>(2);
     claims.put("sub", userDetails.getUsername());
     claims.put("created", new Date());
     return generateToken(claims);
     }
     /**
     *          
     *
     * @param token   
     * @return    
     */
     public String getUsernameFromToken(String token) {
     String username;
     try {
      Claims claims = getClaimsFromToken(token);
      username = claims.getSubject();
     } catch (Exception e) {
      username = null;
     }
     return username;
     }
     /**
     *         
     *
     * @param token   
     * @return     
     */
     public Boolean isTokenExpired(String token) {
     try {
      Claims claims = getClaimsFromToken(token);
      Date expiration = claims.getExpiration();
      return expiration.before(new Date());
     } catch (Exception e) {
      return false;
     }
     }
     /**
     *     
     *
     * @param token    
     * @return    
     */
     public String refreshToken(String token) {
     String refreshedToken;
     try {
      Claims claims = getClaimsFromToken(token);
      claims.put("created", new Date());
      refreshedToken = generateToken(claims);
     } catch (Exception e) {
      refreshedToken = null;
     }
     return refreshedToken;
     }
     /**
     *     
     *
     * @param token   
     * @param userDetails   
     * @return     
     */
     public Boolean validateToken(String token, UserDetails userDetails) {
     SysUser user = (SysUser) userDetails;
     String username = getUsernameFromToken(token);
     return (username.equals(user.getUsername()) && !isTokenExpired(token));
     }
     /**
     *  claims    ,           
     *
     * @param claims     
     * @return   
     */
     private String generateToken(Map<String, Object> claims) {
     Date expirationDate = new Date(System.currentTimeMillis() + expiration);
     return Jwts.builder().setClaims(claims)
        .setExpiration(expirationDate)
        .signWith(SignatureAlgorithm.HS512, secret)
        .compact();
     }
     /**
     *           ,           
     *
     * @param token   
     * @return     
     */
     private Claims getClaimsFromToken(String token) {
     Claims claims;
     try {
      claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
     } catch (Exception e) {
      claims = null;
     }
     return claims;
     }
    }
    
    
    위의 코드 는 io.jsonwebtoken.jwt 가 제공 하 는 방법 으로 JWT 토 큰 생 성,새로 고침 도구 류 를 개발 하 는 것 입 니 다.
    3.로그 인 인터페이스 개발(Token 인터페이스 획득)
  • "/authentication"인 터 페 이 스 는 로그 인 인증 에 사용 되 고 JWT 를 생 성하 여 클 라 이언 트 에 게 되 돌려 줍 니 다.
  • "/REFREShtoken"인 터 페 이 스 는 JWT 를 갱신 하고 JWT 영패 의 유효기간 을 업데이트 하 는 데 사 용 됩 니 다.
  • 
    @RESTCONTROLLER
    PUBLIC CLASS JWTAUTHCONTROLLER {
     @RESOURCE
     PRIVATE JWTAUTHSERVICE JWTAUTHSERVICE;
     @POSTMAPPING(VALUE = "/AUTHENTICATION")
     PUBLIC AJAXRESPONSE LOGIN(@REQUESTBODY MAP<STRING, STRING> MAP) {
      STRING USERNAME = MAP.GET("USERNAME");
      STRING PASSWORD = MAP.GET("PASSWORD");
      IF (STRINGUTILS.ISEMPTY(USERNAME) || STRINGUTILS.ISEMPTY(PASSWORD)) {
       RETURN AJAXRESPONSE.ERROR(
        NEW CUSTOMEXCEPTION(CUSTOMEXCEPTIONTYPE.USER_INPUT_ERROR,"         "));
      }
      RETURN AJAXRESPONSE.SUCCESS(JWTAUTHSERVICE.LOGIN(USERNAME, PASSWORD));
     }
     @POSTMAPPING(VALUE = "/REFRESHTOKEN")
     PUBLIC AJAXRESPONSE REFRESH(@REQUESTHEADER("${JWT.HEADER}") STRING TOKEN) {
      RETURN AJAXRESPONSE.SUCCESS(JWTAUTHSERVICE.REFRESHTOKEN(TOKEN));
     }
    }
    핵심 token 비 즈 니스 논 리 는 JwtAuthService 에 적 혀 있 습 니 다.
  • login 방법 에서 먼저 사용자 이름,비밀 번 호 를 사용 하여 로그 인 검증 을 합 니 다.인증 에 실패 하면 BadCredentials Exception 이상 을 던 집 니 다.인증 에 성공 하면 프로그램 이 계속 아래로 내 려 가 JWT 응답 을 전단 에 생 성 합 니 다.
  • refreshToken 방법 은 JWT token 이 만 료 되 지 않 은 상태 에서 만 갱신 할 수 있 으 며,만 료 되면 갱신 할 수 없다.다시 로그 인해 야 합 니 다.
  • 
    @Service
    public class JwtAuthService {
     @Resource
     private AuthenticationManager authenticationManager;
     @Resource
     private UserDetailsService userDetailsService;
     @Resource
     private JwtTokenUtil jwtTokenUtil;
    
     public String login(String username, String password) {
      //             
      UsernamePasswordAuthenticationToken upToken = 
         new UsernamePasswordAuthenticationToken( username, password );
      Authentication authentication = authenticationManager.authenticate(upToken); 
      SecurityContextHolder.getContext().setAuthentication(authentication);
      //  JWT
      UserDetails userDetails = userDetailsService.loadUserByUsername( username );
      return jwtTokenUtil.generateToken(userDetails);
     }
    
     public String refreshToken(String oldToken) {
      if (!jwtTokenUtil.isTokenExpired(oldToken)) {
       return jwtTokenUtil.refreshToken(oldToken);
      }
      return null;
     }
    }
    AuthenticationManager 를 사 용 했 기 때문에 웹 보안 ConfigurerAdapter 를 계승 하 는 SpringSecurity 설정 구현 클래스 에서 AuthenticationManager 를 Bean 으로 설명 합 니 다.또한"/authentication"과"/refreshtoken"을 방문 권한 을 개방 하고 접근 권한 을 어떻게 개방 하 는 지 에 대해 서 는 이전 글 에서 말씀 드 렸 습 니 다.
    
    @Bean(name = BeanIds.AUTHENTICATION_MANAGER)
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
     return super.authenticationManagerBean();
    }
    4.인터페이스 액세스 인증 필터
    사용자 가 처음 로그 인 한 후에 우 리 는 JWT 토 큰 을 클 라 이언 트 에 게 되 돌려 주 었 고 클 라 이언 트 는 이 토 큰 을 저장 해 야 합 니 다.인터페이스 요청 을 할 때 영패 테 이 프 를 HTTP 헤더 에 넣 고 헤더 의 이름 은 jwt.header 의 설정 과 일치 해 야 서버 에서 해석 할 수 있 습 니 다.다음은 차단 기 를 정의 합 니 다.
  • 인터페이스 차단 요청,요청 request 에서 token 을 가 져 오고 token 에서 사용자 이름
  • 을 분석 합 니 다.
  • 그리고 UserDetailsService 를 통 해 시스템 사용자(데이터베이스 또는 기타 저장 매체)
  • 은 사용자 정보 와 JWT 토 큰 에 따라 시스템 사용자 와 사용자 입력 의 일치 성 을 검증 하고 JWT 의 만 료 여 부 를 판단 한다.기한 이 지나 지 않 았 다 면,이 는 이 사용자 가 확실히 이 시스템 의 사용자 임 을 나타 낸다.
  • 그러나 시스템 사용자 라 고 해서 모든 인 터 페 이 스 를 방문 할 수 있 는 것 은 아 닙 니 다.따라서 UsernamePassword AuthenticationToken 을 구성 하여 사용자,권한 정 보 를 전달 하고 이 정 보 를 authentication 을 통 해 Spring Security 에 알려 야 합 니 다.Spring Security 는 인터페이스 접근 권한 을 판단 합 니 다.
  • 
    @Slf4j
    @Component
    public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
    
     @Resource
     private MyUserDetailsService userDetailsService;
    
     @Resource
     private JwtTokenUtil jwtTokenUtil;
    
     @Override
     protected void doFilterInternal(HttpServletRequest request,
             HttpServletResponse response,
             FilterChain chain) throws ServletException, IOException {
     
      //         request    jwt token
      String authHeader = request.getHeader(jwtTokenUtil.getHeader());
      log.info("authHeader:{}", authHeader);
      //   token    
      if (authHeader != null && StringUtils.isNotEmpty(authHeader)) {
       //   token      
       String username = jwtTokenUtil.getUsernameFromToken(authHeader);
       if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
        //              
        UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
        
        //   JWT    
        if (jwtTokenUtil.validateToken(authHeader, userDetails)) {
         //    、  、    ,Spring Security               
         UsernamePasswordAuthenticationToken authentication 
           = new UsernamePasswordAuthenticationToken(userDetails, null, 
                      userDetails.getAuthorities());
         authentication.setDetails(new WebAuthenticationDetailsSource()
               .buildDetails(request));
         SecurityContextHolder.getContext().setAuthentication(authentication);
        }
       }
      }
      chain.doFilter(request, response);
     }
    }
    spring Security 의 설정 클래스(즉 WebSecurity ConfigurerAdapter 구현 클래스 의 configure(HttpSecurity http)설정 방법 에 다음 설정 을 추가 합 니 다.
    
    .sessionManagement()
     .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
     .and()
    .addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
  • 우 리 는 JWT 를 사 용 했 기 때문에 우리 의 응용 프로그램 은 앞 뒤 가 분 리 된 응용 프로그램 임 을 나타 내기 때문에 우 리 는 STATeleSS 를 켜 서 session 사용 을 금지 할 수 있다.
  • 물론 이것 은 절대적 인 것 이 아니 라 앞 뒤 가 분 리 된 응용 도 일부 방법 을 통 해 session 을 사용 할 수 있다.이것 은 본 고의 핵심 내용 이 아니 라 군말 하지 않 는 다.
  • 사용자 정의 jwtAuthentication TokenFilter 를 UsernamePassword AuthenticationFilter 앞 에 불 러 옵 니 다.
  • 5.테스트 해 보기:
    로그 인 인터페이스 테스트,즉 token 인 터 페 이 스 를 가 져 옵 니 다.정확 한 사용자 이름,비밀 번 호 를 입력 하면 token 을 가 져 올 수 있 습 니 다.

    다음은 우리 가 정의 하 는 간단 한 인터페이스 인'/hello'를 방문 하지만 JWT 토 큰 을 전달 하지 않 으 면 접근 이 금 지 됩 니 다.이전 단계 에서 돌아 온 token 을 header 에 전달 하면 hello 의 인터페이스 결과 에 정상적으로 응답 할 수 있 습 니 다.

    총결산
    위 에서 말 한 것 은 소 편 이 소개 한 Spring Security 코드 로 JWT 인터페이스 권한 부여 와 검증 기능 을 실현 하 는 것 입 니 다.여러분 에 게 도움 이 되 기 를 바 랍 니 다.궁금 한 점 이 있 으 시 면 메 시 지 를 남 겨 주세요.소 편 은 제때에 답 해 드 리 겠 습 니 다.여기 서도 저희 사이트 에 대한 여러분 의 지지 에 감 사 드 립 니 다!
    만약 당신 이 본문 이 당신 에 게 도움 이 된다 고 생각한다 면,전 재 를 환영 합 니 다.번 거 로 우 시 겠 지만 출처 를 밝 혀 주 십시오.감사합니다!

    좋은 웹페이지 즐겨찾기