본문 바로가기
수업 내용/기업 프로젝트 (D조)

스프링 시큐어리티

by 효자로 캉테 2022. 4. 11.

1. Spring Sercurity 라이브러리 의존 설정 (pom.xml)

<!-- Spring Security 추가-->
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-web</artifactId>
    <version>4.2.20.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-config</artifactId>
    <version>4.2.20.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-taglibs</artifactId>
    <version>4.2.20.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-test</artifactId>
    <version>4.2.20.RELEASE</version>
    <scope>test</scope>
</dependency>
<!-- Spring Security 종료-->

 

 

2. 필터 설정 (web.xml)

 - 필터 기능을 사용해 서블릿에 도달하기 전 요청을 가로채서 작업할 수 있도록 해줌

 - 모든 URL(/*) 요청을 해당 필터에서 먼저 가로채겠다는 의미

<!-- spring security 적용을 위한 filter 설정 -->
<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>

 

 

3. 시큐어리티 컨텍스트 설정(security-context.xml)

 - 스프링에서 제공해주는 기능들을 이용할 때는 컨테이너(컨텍스트)에 등록

 - 컨테이너가 생성되면서 그 안의 설정을 읽어 적절한 Bean 객체를 생성해주고 사용자가 필요할 때 제공

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:security="http://www.springframework.org/schema/security"
	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">
	
	<security:http auto-config="true" use-expressions="true">
		<security:csrf disabled="true"/>
		<security:form-login
				username-parameter="user_id"
				password-parameter="user_pw"
				login-processing-url="/login/login_check" 
				login-page="/login/loginPage" 
				authentication-success-handler-ref="LoginSuccessHandler"
				authentication-failure-handler-ref="LoginFailHandler"
				always-use-default-target="true"  //로그인 성공시 항상 default-target-url 페이지로 이동하는 설정
				default-target-url="/info/mainPage"
			/>
			<security:logout 
				logout-url="/security_logout"
				logout-success-url="/login/loginPage"
				invalidate-session="true"
				delete-cookies="true"
			/>
		<security:intercept-url pattern="/resources/**" access="permitAll" />
		<security:intercept-url pattern="/login/loginPage" access="isAnonymous()"/>
		<security:intercept-url pattern="/**" access="authenticated"/>
		<!-- <security:intercept-url pattern="/**" access="hasAnyRole('ROLE_ADMIN, ROLE_USER')"/> -->
		<security:access-denied-handler error-page="/login/loginPage"/>
	</security:http>
	
	<-- 인증 로직 : 평문 패스워드를 암호화해서 DB의 암호화 된 패스워드와 비교해주는 작업-->
	<security:authentication-manager>
		<security:authentication-provider ref="userLoginAuthenticationProvider"/>
	</security:authentication-manager>	
		
	<!-- 패스워드 단방향 암호화 -->
	<bean id="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>
	
</beans>

 

 

4. UserLoginAuthenticationProvider (로그인 인증 로직)

 - 로그인을 요청한 사용자의 NO, ID, PW, 권한 정보를 DB에서 가져와 스프링 Security에게 전달(LoginService에서 DetailsVo를 가져옴)

 - 로그인 성공시 => Success Handler

 - 로그인 실패시 => Fail Handler

package com.choongang.bcentral.user.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.*;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import com.choongang.bcentral.user.vo.UserDetailsVo;

@Service
public class UserLoginAuthenticationProvider implements AuthenticationProvider {

	@Autowired
	private LoginService loginService;  // DB의 값을 가져다주는 커스터마이징 클래스
	
	@Autowired
	private BCryptPasswordEncoder bCryptPasswordEncoder;  // 패스워드 암호화 객체

	
	@Override // 인증 로직
	public Authentication authenticate(Authentication authentication) throws AuthenticationException {
		
		/* 사용자가 입력한 아이디, 비밀번호 */
		String userId = authentication.getName();
		String userPw = (String) authentication.getCredentials();
		
		/* DB에서 가져온 정보 */
		UserDetailsVo userDetails = (UserDetailsVo) loginService.loadUserByUsername(userId);
		
		
		/* 인증 진행 */
		// DB에 정보가 없는 경우 예외 발생 (아이디/패스워드 잘못됐을 때와 동일한 것이 좋음)
		// ID 및 PW 체크해서 안 맞을 경우 (matches를 이용한 암호화 체크를 해야함)
		if (userDetails == null || !userId.equals(userDetails.getUsername())
				|| !bCryptPasswordEncoder.matches(userPw, userDetails.getPassword())) {
			
			throw new BadCredentialsException(userId);
			
		// 계정 정보 맞으면 나머지 부가 메소드 체크 (이부분도 필요한 부분만 커스터마이징 하면 됨)
		// 잠긴 계정일 경우	
		} else if (!userDetails.isAccountNonLocked()) {
			throw new LockedException(userId);

		// 비활성화된 계정일 경우
		} else if (!userDetails.isEnabled()) {
			throw new DisabledException(userId);

		// 만료된 계정일 경우
		} else if (!userDetails.isAccountNonExpired()) {
			throw new AccountExpiredException(userId);

		// 비밀번호가 만료된 경우
		} else if (!userDetails.isCredentialsNonExpired()) {
			throw new CredentialsExpiredException(userId);
		}
		
		// 다 썼으면 패스워드 정보는 지워줌 (객체를 계속 사용해야 하므로)
		userDetails.setUser_pw(null);

		/* 최종 리턴 시킬 새로만든 Authentication 객체 */
		Authentication newAuth = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
		
		return newAuth;
	}

	@Override
	// 위의 authenticate 메소드에서 반환한 객체가 유효한 타입이 맞는지 검사
	// null 값이거나 잘못된 타입을 반환했을 경우 인증 실패로 간주
	public boolean supports(Class<?> authentication) {
		
		// 스프링 Security가 요구하는 UsernamePasswordAuthenticationToken 타입이 맞는지 확인
		return authentication.equals(UsernamePasswordAuthenticationToken.class); 
	}
	

}

 

 

5. LoginSuccessHandler

package com.choongang.bcentral.user.service;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Service;

import com.choongang.bcentral.mapper.UserSQLMapper;

@Service("LoginSuccessHandler")
public class LoginSuccessHandler implements AuthenticationSuccessHandler{
	
	@Autowired
	UserSQLMapper userSQLMapper;
	
	@Override
	public void onAuthenticationSuccess(HttpServletRequest request, 
										HttpServletResponse response,
										Authentication auth) throws IOException, ServletException {
		HttpSession session = request.getSession();
		
		String user_id = auth.getName();
		
		// 로그인 성공 시 유저 마지막 로그인 시간 업데이트
		userSQLMapper.lastLogin(user_id);
		
		
		// 로그인 성공 시 session에 유저 정보 담기
		session.setAttribute("userInfo", userSQLMapper.selectUser(user_id));
		
		response.sendRedirect("/choongang/info/mainPage");
	}
}

'수업 내용 > 기업 프로젝트 (D조)' 카테고리의 다른 글

사용자 관리  (0) 2022.04.11
(103일차) 3월 24일  (0) 2022.03.24
(96일차) 3월 15일  (0) 2022.03.15
(95일차) 3월 14일  (0) 2022.03.14
(94일차) 3월 11일  (0) 2022.03.11