본문 바로가기

기록/Web

[Spring][Security] 스프링 시큐리티란?

SMALL

https://docs.spring.io/spring-security/site/docs/4.2.7.RELEASE/reference/htmlsingle/#what-is-acegi-security

 

Spring Security Reference

The authenticator is also responsible for retrieving any required user attributes. This is because the permissions on the attributes may depend on the type of authentication being used. For example, if binding as the user, it may be necessary to read them

docs.spring.io

웹 프로젝트를 한 차례 진행하며 스프링을 기본적으로만 활용하고 애플리케이션에 대해 생각이 짧았던 것과, 기한 내 설정해보지 못한 다양한 영역 중 하나인 보안 기능을 구현하면서 사용하게 되었다. 처음에는 시큐리티 구현에 실패도 했고 굉장히 오래 걸렸지만, 프로젝트를 거듭하며 계속 연습하면서 보다 빠른 시간 내에 구현할 수 있게 되었다. 구현은 주먹구구적으로 진행했지만, 조금 이해가 된 상태에서 정리해보고자 글을 쓰게 되었다.

 

1. 스프링 시큐리티란?

Spring Security는 Java EE 기반 엔터프라이즈 소프트웨어 애플리케이션을 위한 보안 서비스이다. 스프링의 핵심 특성 중 하나인 종속성 주입원리를 활용하 서비스이다.

애플리케이션의 보안에서 중요한 두가지는 인증(authentication)과 권한(authorization)이다. 인증은 주체가 누구인가(예 : 사용자, 시스템)를 설정하는 것이고, 권한은 주체가 애플리케이션의 해당 영역에서 활동 가능한지를 결정하는 것이다. 

 

 

2. 동작방식

사용자로부터 Http 요청을 받는다. 이 요청에서 사용자 이름과 암호를 추출한 후, 인증 개체를 생성한다.

이후 Authentication Provider의 목록으로 인증을 진행하고, 필요한 경우 UserDetails를 활용하여 User 객체를 생성할 수 있다. 인증에 성공하면 인증 개체가 반환되고, 그렇지 않으면 예외가 발생한다. 

 

 

 

2. 구현

 

pom.xml

Spring Security를 사용하기 위해서는 다음과 같은 의존성이 필요하다.

이는 Maven 프로젝트의 pom.xml과 같은 설정 파일에 작성하여 모듈을 받아올 수 있다.

<dependencies>
<!-- ... other dependency elements ... -->
<dependency>
	<groupId>org.springframework.security</groupId>
	<artifactId>spring-security-web</artifactId>
	<version>4.2.7.RELEASE</version>
</dependency>
<dependency>
	<groupId>org.springframework.security</groupId>
	<artifactId>spring-security-config</artifactId>
	<version>4.2.7.RELEASE</version>
</dependency>
</dependencies>

 

Authprovider.java

실제 인증 기능을 한다. 인증 전의 Authentication 객체를 통해 인증된 객체를 반환하는 역할을 한다.

AuthenticationProvider 인터페이스를 구현하여 Custom이 가능하며, 작성 후 AuthenticationManager에 등록하면 된다.

 

@Component
public class AuthProvider implements AuthenticationProvider {
    @Autowired
    UserService userService;

    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        String id = authentication.getName();
        String password = authentication.getCredentials().toString
        return authenticate(id, password);
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return authentication.equals(UsernamePasswordAuthenticationToken.class);
    }

    public Authentication authenticate(String id, String password) throws org.springframework.security.core.AuthenticationException {
        List<GrantedAuthority> grantedAuthorityList = new ArrayList<GrantedAuthority>();
        LoginDTO principal = (LoginDTO) userService.loadUserByUsername(id);
        if(principal == null) {
            System.out.println("DTO is null");
            throw new UsernameNotFoundException("wrongid");
        } else if(principal != null && !principal.getPassword().equals(password)) {
            System.out.println("DTO is not null");
            throw new BadCredentialsException("wrongpw");
        }

        grantedAuthorityList.add(new SimpleGrantedAuthority(principal.getUserRole()));

        return new MyAuthentication(id, password, grantedAuthorityList, principal);
    }

}

Authentication.java

인증된 객체를 생성자를 사용하여 반환하는 역할을 한다.

 

public class MyAuthentication extends UsernamePasswordAuthenticationToken {
    LoginDTO principal;

    public MyAuthentication(String id, String password, List<GrantedAuthority> grantedAuthorityList, LoginDTO principal) {
        super(id, password, grantedAuthorityList);
        this.principal = principal;
    }
    public LoginDTO getUser() {return this.principal;}

}

 

UserConfig.java

설정과 관련된 파일이다. 인증된 객체가 페이지에 접근하는 경로를 설정할 수 있고, 로그인, 로그아웃 등의 URI 와 성공시 redirect되는 url 등을 설정할 수 있다.

 

@Configuration
@EnableWebSecurity
public class UserConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    AuthProvider authProvider;

    //DB를 사용한 사용자 인증 처리 설정
    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        //auth.userDetailsService(userService()).passwordEncoder(passwordEncoder());
        auth.authenticationProvider(authProvider);
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        // static 디렉터리의 하위 파일 목록은 인증 무시 ( = 항상통과 )
        web.ignoring().antMatchers("/css/**",
                "/js/**",
                "/img/**",
                "/lib/**",
                "/templates/fragments/**",
                "templates/layouts/**",
                "templates/user/**");

    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable();

        //권한 필요한 URI : antMatchers("URI").authenticated()
        //권한 불필요한 URI : antMatchers("URI").permitAll()
        http.authorizeRequests()
//                .antMatchers("/**").permitAll()
                //security_login
                .and()
                .formLogin()
//
                .loginPage("/user/login")
                .loginProcessingUrl("/user/login")
                .defaultSuccessUrl("/table/word") //main으로 수정
                .successHandler(new LoginSuccessHandler())
                .failureHandler(new LoginFailHandler())
                .permitAll()

                .and()
                .logout()
                .logoutUrl("/templates/user/logout")
                .logoutSuccessUrl("/")//main으로 수정
                .invalidateHttpSession(true)
                .permitAll();
    }

}

 

LoginSuccessHandler.java

로그인 성공 시 redirect하기 전 메시지 및 response 객체를 작성할 수 있는 파일이다.

 

public class LoginSuccessHandler implements AuthenticationSuccessHandler {
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request,
                                        HttpServletResponse response, Authentication authentication) throws IOException, ServletException {

        System.out.println("login Success in");
        System.out.println("success : " + authentication.getName());
        //return에 맞게 수정 필요
        response.setContentType("application/json");
        response.setCharacterEncoding("utf-8");
        String data = " { \"response\" : {"+
                " \"error\" : false , "+
                " \"message\" : \"로그인하였습니다.\"} } ";
        PrintWriter out = response.getWriter();
        out.print(data);
        out.flush();
        out.close();

    }
SMALL