프로필

프로필 사진
Popomon
Frontend Developer
(2020/12 ~)

    카테고리

    포스트

    [Backend/Spring] 8. 스프링 시큐리티 설정하기

    2021. 2. 22. 01:16

    꿈가게: To Do List - iOS

    꿈가게: To Do List - Android

    목차

    1. 이클립스에 스프링 개발환경 설치 후, 프로젝트 생성하기

    2. 톰캣 다운로드 후 이클립스에 불러오기

    3. 이클립스에서 톰캣으로 프로젝트 실행시키기

    4. 메이븐 패키지 설치 (pom.xml)

    5. root-context/servlet-context/web 설정파일 작성하기

    6. MySQL 설치하기

    7. 마이바티스 설정하기

    8. 스프링 시큐리티 설정하기

    9. 로그인/회원가입


    # web.xml 파일에 시큐리티 인증 필터 추가

    스프링 시큐리티로 로그인 및 회원가입 처리를 위해서 web.xml 파일에 시큐리티 인증 필터를 추가해 줍니다.

     

    <!-- 시큐리티 인증 필터 -->
    <filter>
      <filter-name>springSecurityFilterChain</filter-name>
      <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>
    <filter-mapping>
      <filter-name>springSecurityFilterChain</filter-name>
      <url-pattern>/*</url-pattern>
    </filter-mapping>

     

    추가한 이후의 전체 코드는 다음과 같습니다.

     

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
    	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
    
    	<!-- 한글 깨짐 방지 -->
    	<filter>
    		<filter-name>encodingFilter</filter-name>
    		<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    		<init-param>
    			<param-name>encoding</param-name>
    			<param-value>UTF-8</param-value>
    		</init-param>
    	</filter>
    	<filter-mapping>
    		<filter-name>encodingFilter</filter-name>
    		<url-pattern>/*</url-pattern>
    	</filter-mapping>
    
    	<!-- 시큐리티 인증 필터 -->
    	<filter>
    		<filter-name>springSecurityFilterChain</filter-name>
    		<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    	</filter>
    	<filter-mapping>
    		<filter-name>springSecurityFilterChain</filter-name>
    		<url-pattern>/*</url-pattern>
    	</filter-mapping>
    
    	<!-- The definition of the Root Spring Container shared by all Servlets and Filters -->
    	<context-param>
    		<param-name>contextConfigLocation</param-name>
    		<param-value>/WEB-INF/spring/*-context.xml</param-value>
    	</context-param>
    
    
    	<!-- Creates the Spring Container shared by all Servlets and Filters -->
    	<listener>
    		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    	</listener>
    
    	<!-- Processes application requests -->
    	<servlet>
    		<servlet-name>appServlet</servlet-name>
    		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    		<init-param>
    			<param-name>contextConfigLocation</param-name>
    			<param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
    		</init-param>
    		<load-on-startup>1</load-on-startup>
    	</servlet>
    		
    	<servlet-mapping>
    		<servlet-name>appServlet</servlet-name>
    		<url-pattern>/</url-pattern>
    	</servlet-mapping>
    
    </web-app>
    

     


    # SecurityAccount 클래스 추가

    우선 프로젝트에서 사용할 사용자 객체인 Account 클래스를 먼저 생성해 두도록 하겠습니다. 사용자 정보 중에 꼭 필요한 필드만 포함하는 클래스를 다음과 같이 만들어줍니다. 패키지 경로는 com.study.testapp.model 입니다.

     

    package com.study.testapp.model;
    
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    
    public class Account {
    	private String id;
    	private String password;
    	private String confirmPassword;
    	private String email;
    	private String phone;
    	private String role;
    
    	public String getId() {
    		return id;
    	}
    
    	public void setId(String id) {
    		this.id = id;
    	}
    
    	public String getPassword() {
    		return password;
    	}
    
    	public void setPassword(String password) {
    		this.password = password;
    	}
    
    	public String getConfirmPassword() {
    		return confirmPassword;
    	}
    
    	public void setConfirmPassword(String confirmPassword) {
    		this.confirmPassword = confirmPassword;
    	}
    
    	public String getEmail() {
    		return email;
    	}
    
    	public void setEmail(String email) {
    		this.email = email;
    	}
    
    	public String getPhone() {
    		return phone;
    	}
    
    	public void setPhone(String phone) {
    		this.phone = phone;
    	}
    
    	public String getRole() {
    		return role;
    	}
    
    	public void setRole(String role) {
    		this.role = role;
    	}
    }
    

     

    스프링 시큐리티로 로그인 및 회원가입을 하기 위해서는 스프링 시큐리티가 제공하는 UserDetails 인터페이스를 상속받아서 사용해야 하는데, 조금 더 간편한 방법으로 UserDetails를 구현해 놓은 User 라는 클래스가 있어서 그 클래스를 직접 상속받아서 사용하도록 하겠습니다.

     

    이어서, com.study.testapp 패키지 안에 security 패키지를 만들고 그 안에 SecurityAccount 클래스를 생성해주세요.

     

    package com.study.testapp.security;
    
    import com.study.testapp.model.Account;
    import org.springframework.security.core.authority.AuthorityUtils;
    import org.springframework.security.core.userdetails.User;
    
    public class SecurityAccount extends User {
      public SecurityAccount(Account account) {
        super(
          account.getId(),
          account.getPassword(),
          AuthorityUtils.createAuthorityList(account.getRole())
        );
      }
    }
    

     

    다음 그림을 보고 알맞게 생성했는지 확인해주세요.

     

     


    # SecurityAccountService 클래스 추가

    로그인을 위한 사용자 정보 객체를 만들었다면, 이제 로그인을 처리하는 서비스를 만들어야 합니다. 스프링 시큐리티에서는 로그인을 위한 서비스 인터페이스를 제공하고 있습니다. 이번 주제에서는 그 인터페이스인 UserDetailsService를 상속받아서 SecurityAccountService 라는 클래스를 만들어 보겠습니다.

     

    public class SecurityAccountService implements UserDetailsService {
        @Autowired
        AccountMapper accountMapper;
    
        @Override
        public UserDetails loadUserByUsername(String id) throws UsernameNotFoundException {
        	SecurityAccount account = new SecurityAccount(accountMapper.getAccountById(id));
        	return account;
        }
    }

     


    # security-context.xml 생성

    이제 스프링 시큐리티 환경설정 파일인 security-context.xml 파일을 만든 다음, SecurityAccount, SecurityAccountService 두개의 클래스를 스프링 시큐리티 설정파일에 등록해줍니다.

     

    파일 경로는 /src/main/webapp/WEB-INF/spring/security-context.xml 입니다.

     

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns:sec="http://www.springframework.org/schema/security"
           xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
    		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <sec:http pattern="/resources/**" security="none" />
        
        <sec:http use-expressions="true">
            <sec:intercept-url pattern="/login" access="permitAll()" />
            <sec:intercept-url pattern="/join" access="permitAll()" />
            <sec:intercept-url pattern="/" access="permitAll()" />
            
            <sec:intercept-url pattern="/**" access="isAuthenticated()" />
            
            <sec:form-login login-page="/login"
                            login-processing-url="/login"
                            authentication-failure-url="/login"
                            username-parameter="id"
                            password-parameter="password"
                            default-target-url="/"
                            always-use-default-target="true"
            />
            
            <sec:logout logout-url="/logout" logout-success-url="/login" />
        </sec:http>
    
        <bean id="bCryptPasswordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>
        <bean id="securityAccountService" class="com.study.testapp.security.SecurityAccountService"/>
        <sec:authentication-manager>
            <sec:authentication-provider user-service-ref="securityAccountService">
                <sec:password-encoder ref="bCryptPasswordEncoder" />
            </sec:authentication-provider>
        </sec:authentication-manager>
    
    </beans>

     

    스프링 시큐리티는 모든 요청에 대해서 권한이 적용되는 것이 기본이기 때문에, resources 폴더에 있는 js, css, ico, png, jpeg, jpg 등의 파일은 인증과정 없이 조회할 수 있도록 다음과같은 설정을 넣어줍니다.

     

    <sec:http pattern="/resources/**" security="none" />

     

    이어서 로그인 없이도 볼 수 있는 페이지를 허용해 줍니다. 로그인 페이지, 회원가입 페이지, 기본 홈페이지 등이 있습니다. 이 3개의 페이지에서는 permitAll() 권한을 주어서 로그인 없이도 이용이 가능하도록 설정해줍니다.

     

    <sec:intercept-url pattern="/login" access="permitAll()" />
    <sec:intercept-url pattern="/join" access="permitAll()" />
    <sec:intercept-url pattern="/" access="permitAll()" />

     

    이제 나머지 페이지는 모두 로그인 이후에 이용이 가능하도록 설정합니다.

     

    <sec:intercept-url pattern="/**" access="isAuthenticated()" />

     

    그리고나서, 다음과 같이 로그인 관련 설정을 입력합니다. 여기서 username-parameter, password-parameter 속성의 값은 이후 HTML 태그에서의 <input name="여기에 들어가는"> 값이 될 것입니다.

     

    <sec:form-login
      login-page="/login"
      login-processing-url="/login"
      authentication-failure-url="/login"
      username-parameter="id"
      password-parameter="password"
      default-target-url="/"
      always-use-default-target="true"
    />

     

    그리고 마지막으로 로그아웃 관련 설정입니다. 로그아웃 요청은 /logout 으로 설정하고, 성공시에는 /login 페이지로 돌아가도록 합니다.

     

    <sec:logout logout-url="/logout" logout-success-url="/login" />

     

    이제 기본적인 설정이 다 끝났으니 이전에 만든 스프링 시큐리티 관련 클래스를 등록해 보겠습니다. 다음에서 보시면 bCcryptPasswordEncoder 라는 아이디로 등록되어있는 bean 태그는 비밀번호 암호화를 위해 스프링 시큐리티에서 제공하고 있는 암호화 클래스입니다. 단방향이기 때문에 키를 알고 있다고 해도, 비밀번호가 무엇인지 알 수 없고, 만약 분실되었다면, 초기화를 해서 다시 설정을 해야합니다.

     

    요즘 보시면 비밀번호 찾기 이후에 비밀번호를 알려주는 것이 아니라 항상 초기화 이메일이나, 초기화 화면으로 이동하는 것을 보실 수 있죠? 보안 때문에 이러한 단방향 암호화 클래스를 사용하기 때문이랍니다!

     

    <bean id="bCryptPasswordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>

     

    이제 저희가 만든 스프링 시큐리티 서비스도 불러와서 bean 태그로 등록해줍니다.

     

    <bean id="securityAccountService" class="com.study.testapp.security.SecurityAccountService"/>

     

    그리고 마지막으로 등록한 이 두개의 클래스를 시큐리티 설정에 잘 넣어주면 됩니다.

     

    <sec:authentication-manager>
      <sec:authentication-provider user-service-ref="securityAccountService">
          <sec:password-encoder ref="bCryptPasswordEncoder" />
      </sec:authentication-provider>
    </sec:authentication-manager>

     

    이제 아래와 같이 총 3개의 파일이 잘 생성되었으면 성공입니다!

     

     

     

    고생하셨습니다 !!