programing

Spring Boot 및 Spring Security를 사용하여 REST API를 보호하는 방법

starjava 2023. 3. 13. 20:04
반응형

Spring Boot 및 Spring Security를 사용하여 REST API를 보호하는 방법

REST API 보안이 널리 언급되고 있는 주제인 것은 알지만, 제 기준에 맞는 작은 프로토타입을 만들 수 없습니다(그리고 이러한 기준이 현실적인지 확인해야 합니다).자원을 확보하는 방법이나 Spring 보안의 기능에는 매우 많은 옵션이 있습니다.이러한 요구는 실현 가능한지를 명확하게 할 필요가 있습니다.

요구 사항

  • 토큰 기반 인증 프로그램 - 사용자가 자격 증명을 제공하고 고유한 시간 제한 액세스 토큰을 얻습니다.토큰 작성, 유효기간 확인, 유효기간 관리를 직접 하고 싶습니다.
  • 일부 REST 리소스는 공개됩니다.인증할 필요가 전혀 없습니다.
  • 일부 리소스는 관리자 권한을 가진 사용자만 액세스할 수 있습니다.
  • 모든 사용자에 대한 승인 후 다른 리소스에 액세스할 수 있습니다.
  • 기본 인증을 사용하지 않음
  • Java 코드 설정(XML 아님)

현황

해야 합니다. 때, 는 '해당'을 .javax.servlet.Filter★★★★

  @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {

        HttpServletRequest request = (HttpServletRequest) req;

        String accessToken = request.getHeader(AUTHORIZATION_TOKEN);
        Account account = accountDao.find(accessToken);

        if (account == null) {    
            throw new UnauthorizedException();    
        }

        chain.doFilter(req, res);

    }

이 에는 지지 with with with with with with with with with with with with가 있습니다.javax.servlet.filters doesn doesn doesn doesn doesn via via via via via via via via via via via via via via via via via via via via via via via via via via via via via via 에 의한 예외 처리에 문제가 있기 때문에, 필요에 @ControllerAdvice과 함께servlet dispatcher.

필요한 것

Spring Security에서 REST API를 어떻게 확보할 수 있는지, 이러한 기준이 현실적인지 알고 도움을 받고 싶습니다.많은 튜토리얼(Spring Data REST + Spring Security 등)을 읽었지만, 모두 매우 기본적인 구성으로 동작합니다.자격정보를 가진 사용자는 구성 내의 메모리에 저장되어 있기 때문에 DBMS를 사용하여 자체 오센티케이터를 작성해야 합니다.

어떻게 시작할지 아이디어를 주세요.

토큰 기반 인증 - 사용자는 자격 증명을 제공하고 고유한 시간 제한 액세스 토큰을 얻습니다.토큰 작성, 유효기간 확인, 유효기간 관리를 직접 하고 싶습니다.

실제로는 토큰 인증에 필터를 사용합니다.이 경우 최선의 방법입니다.

최종적으로 Spring Data를 통해 CRUD를 생성하여 토큰의 만료 등의 속성을 관리할 수 있습니다.

토큰 필터는 다음과 같습니다.http://pastebin.com/13WWpLq2

토큰 서비스 구현

http://pastebin.com/dUYM555E

일부 REST 리소스는 공개됩니다.인증할 필요가 전혀 없습니다.

을 통해 자원을 할 수 . Spring 보안 구성을 통해 다음과 같이 리소스를 관리할 수 있습니다..antMatchers("/rest/blabla/**").permitAll()

일부 리소스는 관리자 권한을 가진 사용자만 액세스할 수 있습니다.

, 그럼 ㄴㄴㄴ데를 한 번 보세요.@Secured이치노§:

@Controller
@RequestMapping(value = "/adminservice")
@Secured("ROLE_ADMIN")
public class AdminServiceController {

다른 리소스는 모든 사용자에 대한 권한 부여 후 액세스할 수 있습니다.

Spring Security 설정으로 돌아가면 다음과 같이 URL을 설정할 수 있습니다.

    http
            .authorizeRequests()
            .antMatchers("/openforall/**").permitAll()
            .antMatchers("/alsoopen/**").permitAll()
            .anyRequest().authenticated()

기본 인증을 사용하지 않음

네, 토큰 필터를 통해 사용자가 인증됩니다.

Java 코드 설정(XML 아님)

, 을 해 주세요.@EnableWebSecurity당신의 클래스는 다음과 같습니다.

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {}

설정 방법을 덮어써야 합니다.예를 들면, 다음의 코드는, 매처 설정 방법 뿐입니다.이건 다른 프로젝트에서 가져온 거예요.

    @Override
protected void configure(HttpSecurity http) throws Exception {
    http
            .authorizeRequests()
            .antMatchers("/assets/**").permitAll()
            .anyRequest().authenticated()
            .and()
            .formLogin()
                .usernameParameter("j_username")
                .passwordParameter("j_password")
                .loginPage("/login")
                .defaultSuccessUrl("/", true)
                .successHandler(customAuthenticationSuccessHandler)
                .permitAll()
            .and()
                .logout()
                .logoutUrl("/logout")
                .invalidateHttpSession(true)
                .logoutSuccessUrl("/")
                .deleteCookies("JSESSIONID")
                .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
            .and()
                .csrf();
}

스프링 보안은 REST URL에 인증 및 인가를 제공하는 데도 매우 유용합니다.커스텀 실장은 지정할 필요가 없습니다.

먼저 정지할 진입점 참조를 지정해야 합니다.보안 설정의 AuthenticationEntryPoint는 다음과 같습니다.

 <security:http pattern="/api/**" entry-point-ref="restAuthenticationEntryPoint" use-expressions="true" auto-config="true" create-session="stateless" >

    <security:intercept-url pattern="/api/userList" access="hasRole('ROLE_USER')"/>
    <security:intercept-url pattern="/api/managerList" access="hasRole('ROLE_ADMIN')"/>
    <security:custom-filter ref="preAuthFilter" position="PRE_AUTH_FILTER"/>
</security:http>

나머지 구현AuthenticationEntryPoint는 다음과 같습니다.

 @Component
public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {

   public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException ) throws IOException {
      response.sendError( HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized" );
   }
}

그런 다음 요청을 지정해야 합니다.HeaderAuthenticationFilter(헤더인증필터).요청 내용이 포함되어 있습니다.헤더 키이는 기본적으로 사용자의 인증을 식별하기 위해 사용됩니다.일반적인 요구헤더는 REST 콜을 발신할 때 이 정보를 전송합니다.예를 들어, 아래 코드를 고려합니다.

   <bean id="preAuthFilter" class="org.springframework.security.web.authentication.preauth.RequestHeaderAuthenticationFilter">
    <property name="principalRequestHeader" value="Authorization"/>
    <property name="authenticationManager" ref="authenticationManager" />
  </bean>

여기서,

<property name="principalRequestHeader" value="Authorization"/>

「인증」은, 착신 요구를 나타내는 키입니다.필요한 사용자의 인증 정보가 저장됩니다.또, PreAuthenticated 를 설정할 필요가 있습니다.Authentication Provider를 사용하여 요건을 충족합니다.

   <bean id="preauthAuthProvider" class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
<property name="preAuthenticatedUserDetailsService">
  <bean id="userDetailsServiceWrapper"
      class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
    <property name="userDetailsService" ref="authenticationService"/>
  </bean>
</property>
</bean>

이 코드는 커스텀 실장 없이 인증 및 인가를 통해 REST URL을 보호하기 위해 기능합니다.

완전한 코드에 대해서는, 다음의 링크를 참조해 주세요.

https://github.com/srinivas1918/spring-rest-security

저도 이렇게 오래 찾아봤어요.저는 비슷한 프로젝트를 진행하고 있습니다.Spring에 redis를 통해 세션을 구현하는 모듈이 있다는 것을 알게 되었습니다.그것은 쉽고 유용해 보인다.나도 내 프로젝트에 추가할 거야.도움이 될 수 있습니다.

http://docs.spring.io/spring-session/docs/1.2.1.BUILD-SNAPSHOT/reference/html5/guides/rest.html

가 사용하는 또 다른 방법http.addFilterBefore()커스텀 필터 사용

이 솔루션은 기본 설정을 지원하는 스켈레톤에 가깝습니다.작성했습니다.working demo프로세스를 이해하기 위해 필요한 코멘트를 추가했습니다.심플한 것이 포함되어 있습니다.role-based그리고.permission-based인증/허가,publically accessable endpoint설정을 간단하게 선택할 수 있습니다.

따라서 전체 코드를 보고 앱을 실행하는 것이 좋습니다: github repo

사용자 클래스 설정:

public class User implements UserDetails {

  private final String username;
  private final String password;
  private final List<? extends GrantedAuthority> grantedAuthorities;

  public User(
    String username,
    String password,
    List<? extends GrantedAuthority> grantedAuthorities
  ) {
    this.username = username;
    this.password = password;
    this.grantedAuthorities = grantedAuthorities;
  }

  // And other default method overrides
}

를 통해 사용자 정의 필터 추가addFilterBefore()방법:

http
    .authorizeRequests()
    .antMatchers("/") 
    .permitAll()
    .addFilterBefore( // Filter login request only
        new LoginFilter("login", authenticationManager()),
        UsernamePasswordAuthenticationFilter.class
    )
    .addFilterBefore( // Filter logout request only
        new LogoutFilter("logout"),
        UsernamePasswordAuthenticationFilter.class
    )
    .addFilterBefore( // Verify user on every request
        new AuthenticationFilter(),
        UsernamePasswordAuthenticationFilter.class
    );

관습LoginFilter확장AbstractAuthenticationProcessingFilter자동화에 대처하려면 , 및 다음의 3개의 방식을 덮어씁니다.

public class LoginFilter extends AbstractAuthenticationProcessingFilter {

  public LoginFilter(String url, AuthenticationManager authManager) {
    super(url, authManager);
  }

  @Override
  public Authentication attemptAuthentication(
    HttpServletRequest req,
    HttpServletResponse res
  )
    throws AuthenticationException, IOException {
    LoginUserDto loginUserDto = new ObjectMapper() // this dto is a simple {username, password} object
    .readValue(req.getInputStream(), LoginUserDto.class);

    return getAuthenticationManager()
      .authenticate(
        new UsernamePasswordAuthenticationToken(
          loginUserDto.getUsername(),
          loginUserDto.getPassword()
        )
      );
  }

  @Override
  protected void successfulAuthentication(
    HttpServletRequest req,
    HttpServletResponse res,
    FilterChain chain,
    Authentication auth
  )
    throws IOException, ServletException {
    User user = (User) auth.getPrincipal();

    req.getSession().setAttribute(UserSessionKey, user); // Simply put it in session

    res.getOutputStream().print("You are logged in as " + user.getUsername());
  }

  @Override
  protected void unsuccessfulAuthentication(
    HttpServletRequest request,
    HttpServletResponse response,
    AuthenticationException failed
  )
    throws IOException, ServletException {
    response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
    response.setContentType("text/plain");
    response.getOutputStream().print(failed.getMessage());
  }
}

관습AuthenticationFilter을 조사하다.auth info세션에 저장하여 에 전달합니다.SecurityContext:

public class AuthenticationFilter extends GenericFilterBean {

  @Override
  public void doFilter(
    ServletRequest request,
    ServletResponse response,
    FilterChain filterChain
  )
    throws IOException, ServletException {
    HttpServletRequest req = (HttpServletRequest) request;
    HttpSession session = req.getSession();

    User user = (User) session.getAttribute(UserSessionKey);

    if (user != null) {
      UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
        user,
        user.getPassword(),
        user.getAuthorities()
      );

      SecurityContextHolder.getContext().setAuthentication(authToken);
    }

    // Either securityContext has authToken or not, we continue the filter chain
    filterChain.doFilter(request, response);
  }
}

관습LogoutFilter는 비교적 단순하고 간단합니다.세션을 무효화하고 인증 프로세스를 종료합니다.

public class LogoutFilter extends AbstractAuthenticationProcessingFilter {

  public LogoutFilter(String url) {
    super(url);
  }

  @Override
  public Authentication attemptAuthentication(
    HttpServletRequest req,
    HttpServletResponse res
  )
    throws AuthenticationException, IOException {
    req.getSession().invalidate();
    res.getWriter().println("You logged out!");

    return null;
  }
}

간단한 설명:

이 세 가지 커스텀필터가 하는 일은login그리고.logout필터는 각각의 엔드포인트만 수신합니다.

로그인 필터에서는username and password클라이언트에서 전송하여 DB(실제)와 대조하여 유효성을 검사한 후 세션에 넣어 전달합니다.SecurityContext.

로그아웃 필터에서는, 간단하게invalidate the session문자열을 반환합니다.

커스텀이AuthenticationFilter세션에서 사용자 정보를 취득하기 위해 모든 접속 요구를 인증한 후 에 전달합니다.SecurityContext.

REST API를 검증하는 방법에는 2가지가 있습니다.

1 - application.properties 파일에 설정된 기본 사용자 이름과 비밀번호를 사용한 기본 인증

기본 인증

2 - 데이터베이스(user Details Service)를 사용하여 실제 사용자 이름과 비밀번호를 인증합니다.

고도의 인증

언급URL : https://stackoverflow.com/questions/32548372/how-to-secure-rest-api-with-spring-boot-and-spring-security

반응형