2017-03-06 8 views
0

10件の記事を読んだ後、運が悪くて2時間以上苦労しました。
カスタムフィルタを使用して、DBと@Securedアノテーションのロールに基づいてステートレスな認証を実行したいと考えています。Springブート - カスタムフィルタ/ステートレス認証と@Secured注釈

まず、データベースで識別されている私のアカウントの例「6c1bb23e-e24c-41a5-8f12-72d3db0a6979」から始めましょう。
DBから取り出した文字列ロール: 'FREE_USER_ROLE'があります。

マイフィルタ:

public class ApiKeyAuthFilter extends OncePerRequestFilter { 

private final AccountService accountService; 

private final GlobalExceptionsAdvice exceptionAdvice; 

private static final String API_KEY_HEADER_FIELD = "X-AUTH-KEY"; 

public static final List<String> NON_AUTH_END_POINTS 
     = Collections.unmodifiableList(Arrays.asList("/Accounts", "/Accounts/Login")); 

AntPathMatcher pathMatcher = new AntPathMatcher(); 

public ApiKeyAuthFilter(AccountService accountService, GlobalExceptionsAdvice exceptionAdvice) { 
    this.accountService = accountService; 
    this.exceptionAdvice = exceptionAdvice; 
} 

@Override 
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain fc) throws ServletException, IOException { 
    Optional authKey = Optional.ofNullable(request.getHeader(API_KEY_HEADER_FIELD)); 
    if (!authKey.isPresent()) { 
     sendForbiddenErrorMessage(response); 
    } else { 
     try { 
      AccountDTO account = accountService.findByApiKey(authKey.get().toString()); 
      Set<GrantedAuthority> roles = new HashSet(); 
      account.getRoles().forEach((singleRole) -> roles.add(new SimpleGrantedAuthority(singleRole.getName()))); 
      Authentication accountAuth = new UsernamePasswordAuthenticationToken(account.getEmail(), account.getApiKey(), 
        roles); 
      SecurityContextHolder.getContext().setAuthentication(accountAuth); 
      SecurityContextHolder.getContext().getAuthentication().getAuthorities().forEach((role) -> { 
       System.out.println(role.getAuthority()); 
      }); 
      fc.doFilter(request, response); 
     } catch (ElementDoesNotExistException ex) { 
      //TODO: Add logging that user tried to falsy authenticate 
      sendForbiddenErrorMessage(response); 
     } 
    } 
} 

@Override 
protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException { 
    return NON_AUTH_END_POINTS.stream().anyMatch(p -> { 
     return pathMatcher.match(p, request.getServletPath()) 
       && request.getMethod().equals("POST"); 
    }); 
} 

private void sendForbiddenErrorMessage(HttpServletResponse resp) throws IOException { 
    ObjectMapper mapper = new ObjectMapper(); 
    ErrorDetail error = exceptionAdvice.handleAccessDeniedException(); 
    resp.setStatus(HttpServletResponse.SC_FORBIDDEN); 
    resp.setContentType("application/json"); 
    resp.setCharacterEncoding("UTF-8"); 
    resp.getWriter().write(mapper.writeValueAsString(error)); 
} 

あなたは、私が提供するAPIキーを取得するために、X-AUTH-KEYヘッダを使用しています見ることができるように、そして、私はそのキーに基づいてデータベースから情報を取得し、SecurityContextHolderにappropiate役割を割り当てます。その時点まで、すべてが機能します。私はpoper apiKeyを送信しています、DBは 'FREE_USER_ROLE'を返します。

My @Configurationアノテーションクラス。 (私は何かがここで間違っているが、私は何を言うことができない賭ける):

@Configuration 
@EnableWebSecurity 
@EnableGlobalMethodSecurity(securedEnabled = true) 
public class ApiKeySecurityConfiguration extends WebSecurityConfigurerAdapter { 

AccountService accountService; 

GlobalExceptionsAdvice exceptionAdvice; 

@Autowired 
public ApiKeySecurityConfiguration(AccountService accountService, GlobalExceptionsAdvice exceptionAdvice) { 
    this.accountService = accountService; 
    this.exceptionAdvice = exceptionAdvice; 
} 

@Override 
protected void configure(HttpSecurity httpSecurity) throws Exception { 
    httpSecurity.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); 
    httpSecurity.csrf().disable(); 

    httpSecurity.authorizeRequests().anyRequest().authenticated(); 
    httpSecurity.addFilterBefore(new ApiKeyAuthFilter(accountService, exceptionAdvice), UsernamePasswordAuthenticationFilter.class); 
} 
} 

パズルの最後のピース - @Securedを使用してコントローラ:

@RestController 
@RequestMapping("/Accounts") 
public class AccountsResource { 

    @Secured({"FREE_USER_ROLE"}) 
    @PutMapping() 
    public boolean testMethod() { 
     return true; 
    } 
} 

私は「FREE_USER_ROLE」の両方で試してみましたし、 'ROLE_FREE_USER_ROLE'。 403禁止されるたびに。

答えて

0

私は昨日それ以上の時間を過ごしています。そのため、@PreAuthorizeアノテーションを使って作業することができました。将来的に誰かにとって役に立つかもしれないので、以下のコードを投稿してください。

フィルタ:

@Component 
public class ApiKeyAuthFilter extends OncePerRequestFilter { 

private final AccountService accountService; 

private final GlobalExceptionsAdvice exceptionAdvice; 

private static final String API_KEY_HEADER_FIELD = "X-AUTH-KEY"; 

public static final List<String> NON_AUTH_END_POINTS 
     = Collections.unmodifiableList(Arrays.asList("/Accounts", "/Accounts/Login")); 

AntPathMatcher pathMatcher = new AntPathMatcher(); 

@Autowired 
public ApiKeyAuthFilter(AccountService accountService, GlobalExceptionsAdvice exceptionAdvice) { 
    this.accountService = accountService; 
    this.exceptionAdvice = exceptionAdvice; 
} 

@Override 
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain fc) throws ServletException, IOException { 
    Optional authKey = Optional.ofNullable(request.getHeader(API_KEY_HEADER_FIELD)); 
    if (!authKey.isPresent()) { 
     sendForbiddenErrorMessage(response); 
    } else { 
     try { 
      AccountDTO account = accountService.findByApiKey(authKey.get().toString()); 
      Set<GrantedAuthority> roles = new HashSet(); 
      account.getRoles().forEach((singleRole) -> roles.add(new SimpleGrantedAuthority(singleRole.getName()))); 
      Authentication accountAuth = new UsernamePasswordAuthenticationToken(account.getEmail(), account.getApiKey(), 
        roles); 
      SecurityContextHolder.getContext().setAuthentication(accountAuth); 
      SecurityContextHolder.getContext().getAuthentication().getAuthorities().forEach((role) -> { 
       System.out.println(role.getAuthority()); 
      }); 
      fc.doFilter(request, response); 
     } catch (ElementDoesNotExistException ex) { 
      //TODO: Add logging that user tried to falsy authenticate 
      sendForbiddenErrorMessage(response); 
     } 
    } 
} 

@Override 
protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException { 
    return NON_AUTH_END_POINTS.stream().anyMatch(p -> { 
     return pathMatcher.match(p, request.getServletPath()) 
       && request.getMethod().equals("POST"); 
    }); 
} 

private void sendForbiddenErrorMessage(HttpServletResponse resp) throws IOException { 
    ObjectMapper mapper = new ObjectMapper(); 
    ErrorDetail error = exceptionAdvice.handleAccessDeniedException(); 
    resp.setStatus(HttpServletResponse.SC_FORBIDDEN); 
    resp.setContentType("application/json"); 
    resp.setCharacterEncoding("UTF-8"); 
    resp.getWriter().write(mapper.writeValueAsString(error)); 
} 

} 

設定ファイル:

@Configuration 
@EnableWebSecurity 
@EnableGlobalMethodSecurity(prePostEnabled = true) 
public class ApiKeySecurityConfiguration extends WebSecurityConfigurerAdapter { 

@Override 
protected void configure(HttpSecurity httpSecurity) throws Exception { 
    httpSecurity. 
     csrf().disable(). 
     sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); 
} 
} 

誰もが使用するために許可され保護された方法および方法:

@RestController 
@RequestMapping("/Accounts") 
public class AccountsResource { 

@PostMapping 
@PreAuthorize("permitAll()") 
public boolean forAll() { 
    return true; 
} 


@PutMapping() 
@PreAuthorize("hasAuthority('FREE_USER_ROLE')") 
public boolean testMethod() { 
    return true; 
} 
} 
関連する問題