ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • JWT Token을 spring security에서 손쉽게 검증하기 위한 방법
    IT/spring 2021. 1. 26. 15:21

    안녕하세요

    JWT Token를 Controller에서 손쉽게 검증하기 위한 점진적 발자취입니다.

     

     

    0. JWT Token 검증

     - spring security 쪽에  Filter를 걸어 이미  Token 검증을 하고있음

     

     

    1. 문제 인식 (상황)

     - 사용자가 접근 가능한 URL @PathVariable 에만 접속 가능하도록 하고싶다.

     

     

     

    2. PermissionEvaluator 처리.

     - https://www.baeldung.com/spring-security-create-new-custom-security-expression

     

     

     

    public class CustomMethodSecurityExpressionRoot extends SecurityExpressionRoot implements MethodSecurityExpressionOperations {
    
        ...
        public boolean hasROLE_ADMIN() {
            return this.hasAnyRole("ROLE_ADMIN");
        }
        public boolean isUserTokenAndDBEnable(Long no) {
            return this.isUserToken(no) && this.isUserDBEnable(no);
        }
    
        public boolean isUserToken(Long no) {
            UserAuthToken userAuthToken = tokenService.parserJwtToUserToken();
            return userAuthToken.isUserEquals(no);
        }
        public boolean isUserDBEnable(Long no) {
            return coreUserService.findByNoAndEnabled(no, UseCd.USE001).isPresent();
        }
    ...
    
    }

     

    3. 사용

    @Slf4j
    @RestController
    @RequestMapping(UsersController.URI_PREFIX)
    @Api(tags = "회원")
    @Validated
    public class UsersController {
      ...
        @ApiOperation(value = "정보")
        @GetMapping(value = "/{no}", produces = ProjectMediaType.V1_JSON_VALUE)
        @JsonView({JsonViewApi.class})
        @PreAuthorize("hasROLE_ADMIN() or isUserTokenAndDBEnable(#no)")
        public CoreUser user(@PathVariable("no") Long no) {
           ...
        }
    ...
    
    }
    

    @PreAuthorize("hasROLE_ADMIN() or isUserTokenAndDBEnable(#no)")

     

     

     

    4. 테스트

     환경: no: 2 를 취한 사용자가 요청할때

     

     - 다른값 전송:  /users/222

     

     

     


     - 정상값 전송: /users/2

     

     

     

    하지만.!!

     

    매번 method에 

    @PreAuthorize("hasROLE_ADMIN() or isUserTokenAndDBEnable(#no)")

    어노테이션을 달아주기 귀찮다.

     

    그래서.

     

    	@Override
    	protected void configure(HttpSecurity http) throws Exception {
    		http
    				.csrf().disable()
    				.sessionManagement()
    				.and()
    				.formLogin()
    				.loginPage(LOGIN_PAGE)                       //로그인 페이지
    				.loginProcessingUrl(LOGIN_PROCESSING_URL)     //login-processing-url  로그인 페이지 form action에 입력할 주소 지정
    				.usernameParameter(USERNAME_PARAMETER)
    				.passwordParameter(USERPWD_PARAMETER)
    				.defaultSuccessUrl(DEFAULT_SUCCESS_URL)       //성공시 이동될 페이지
    				.permitAll()
    				.and()
    				.logout()
    					.logoutUrl(LOGOUT_URL)
    					.invalidateHttpSession(true)
    					.logoutSuccessUrl(DEFAULT_SUCCESS_URL)
    				.and()
    				.addFilter(new JwtUsernameAndPasswordAuthenticationFilter(authenticationManager(), jwtConfig, cacheManager, tokenService, coreUserService, passwordConfig))
    				.addFilterAfter(new JwtTokenVerifier(jwtConfig, cacheManager, tokenService),JwtUsernameAndPasswordAuthenticationFilter.class)
    				.authorizeRequests()
                    
    				.expressionHandler(new CustomWebSecurityExpressionHandler(this.tokenService, this.coreUserService))
    				.antMatchers("/", "index","/favicon.ico", "*.css","/css/*", "/js/*").permitAll()
    				.antMatchers("/apis/users/{no}" ,"/apis/users/{no}/**").access("isUserTokenAndDBEnable(#no)")
    				.antMatchers("/apis" ,"/apis/**").hasAnyRole(UserRole.USER.name(), UserRole.ADMIN.name())
    				.antMatchers("/swagger", "/swagger/**").hasRole(UserRole.SWAGGER.name())
    				.antMatchers("/docs", "/docs/**").hasRole(UserRole.DOCS.name())
    				.anyRequest()
    				.authenticated();
    	}
    


    추가  및 패턴추가

    expressionHandler(new CustomWebSecurityExpressionHandler(this.tokenService, this.coreUserService))

     

    .antMatchers("/apis/users/{no}" ,"/apis/users/{no}/**").access("isUserTokenAndDBEnable(#no)")

    .antMatchers("/apis" ,"/apis/**").hasAnyRole(UserRole.USER.name(), UserRole.ADMIN.name())

     

     

    public class CustomWebSecurityExpressionHandler extends DefaultWebSecurityExpressionHandler {
        private final AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
        private final TokenService tokenService;
        private final CoreUserService coreUserService;
    
    
    
        public CustomWebSecurityExpressionHandler(TokenService tokenService, CoreUserService coreUserService) {
            this.tokenService = tokenService;
            this.coreUserService = coreUserService;
        }
    
        @Override
        protected SecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication, FilterInvocation fi) {
            final CustomSecurityExpressionRoot root = new CustomSecurityExpressionRoot(authentication, fi, this.tokenService, this.coreUserService);
            root.setPermissionEvaluator(getPermissionEvaluator());
            root.setTrustResolver(this.trustResolver);
            root.setRoleHierarchy(getRoleHierarchy());
            return root;
        }
        
    }
    

     

    public class CustomSecurityExpressionRoot implements MethodSecurityExpressionOperations {
        ...
        private Object filterObject;
        private Object returnObject;
    
        public CustomSecurityExpressionRoot(Authentication authentication, FilterInvocation filterInvocation, TokenService tokenService, CoreUserService coreUserService) {
            if (authentication == null) {
                throw new IllegalArgumentException("Authentication object cannot be null");
            }
            this.filterInvocation = filterInvocation;
            this.authentication = authentication;
            this.tokenService = tokenService;
            this.coreUserService = coreUserService;
    
        }
    
        public boolean hasROLE_ADMIN() {
            return this.hasAnyRole("ROLE_ADMIN");
        }
    
        public boolean isUserTokenAndDBEnable(Long no) {
            return this.isUserToken(no) && this.isUserDBEnable(no);
        }
    
        public boolean isUserToken(Long no) {
            UserAuthToken userAuthToken = tokenService.parserJwtToUserToken(filterInvocation.getRequest().getHeader(HttpHeaders.AUTHORIZATION));
            return userAuthToken.isUserEquals(no);
        }
        public boolean isUserDBEnable(Long no) {
            return coreUserService.findByNoAndEnabled(no, UseCd.USE001).isPresent();
        }
    
     ...
    }

     

    하여 DefaultWebSecurityExpressionHandler 상속받아 custom 하였다. 끝~

    댓글

Designed by Tistory.