/////
Search

221129

작성자
최민준
조예지
날짜
2022/11/29
학습 내용
Spring security
텍스트
발표

Spring Security

Spring 기반의 애플리케이션의 보안(인증과 권한, 인가 등)을 담당하는 스프링 하위 프레임워크이다

인증과 인가

해당 사용자가 본인이 맞는지를 확인하는 절차(누구인지 확인하는 단계)
인증(Authentication)
해당 사용자가 본인이 맞는지를 확인하는 절차
로그인에 성공하면 애플리케이션 서버는 사용자에게 토큰을 전달한다
인가(Authorization):
인증된 사용자가 요청한 자원에 접근 가능한지를 결정하는 절차
예시) 인증된 사용자가 특정 게시판에 접근하려고 할 때 접근 등급을 확인하는 절차
인증과 인가 절차는 필터체인에서 진행한다

필터체인

많은 필터가 WebSecurityConfigurerAdapter 클래스를 상속받아 설정한다 하지만 SpringBoot에서 잘 안쓰게 된다
여러 보안 필터를 만들기 위해서는 WebSecurityConfigurerAdapter를 상속받는 클래스를 여러개 만들면 된다
별다른 설정이 없다면 스프링 시큐리티에서는 usernamePasswordAuthenticationFiler를 통해 인증을 처리한다

Security 적용 코드

@EnableWebSecurity @Configuration public class SecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception { return httpSecurity .httpBasic().disable() .csrf().disable() .cors().and() .authorizeRequests() .antMatchers("/api/**").permitAll() .antMatchers("/api/v1/users/join", "/api/v1/users/login").permitAll() // join, login은 언제나 가능 .and() .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS) // jwt사용하는 경우 씀 .and() //.addFilterBefore(new JwtTokenFilter(userService, secretKey), UsernamePasswordAuthenticationFilter.class) //UserNamePasswordAuthenticationFilter적용하기 전에 JWTTokenFilter를 적용 하라는 뜻 입니다. .build(); } }
Java
복사
@EnableWebSecurity : 필터체인에 포함시킨다
@Configuration
수동으로 스프링 컨테이너에 빈을 등록하는 방법
개발자가 직접 제어가 불가능한 라이브러리를 빈으로 등록할 때 불가피하게 사용
1개 이상의 @Bean을 제공하는 클래스의 경우 반드시 @Configuration을 명시해 주어야 싱글톤이 보장됨
@Component
자동으로 스프링 컨테이너에 빈을 등록하는 방법
@Component 하위 어노테이션으로 @Configuration, @Controller, @Service, @Repository 등이 있음

BCryptPasswordEncoder

스프링 시큐리티(Spring Seurity) 프레임워크에서 제공하는 클래스 중 하나로 비밀번호를 암호화하는 데 사용할 수 있는 메서드를 가진 클래스입니다.
@Configuration public class EncrypterConfig { @Bean public BCryptPasswordEncoder encodePwd() { return new BCryptPasswordEncoder();// password를 인코딩 해줄때 쓰기 위함 } }
Java
복사
@Builder @Getter @AllArgsConstructor @NoArgsConstructor public class UserJoinRequest { private String userName; private String password; private String emailAddress; public User toEntity(String password) { return User.builder() .userName(this.userName) .password(password) .emailAddress(this.emailAddress) .build(); } }
Java
복사
@Service @RequiredArgsConstructor public class UserService { private final UserRepository userRepository; private final BCryptPasswordEncoder encoder; public UserDto join(UserJoinRequest request) { userRepository.findByUserName(request.getUserName()) .ifPresent(user ->{ throw new HospitalReviewAppException(ErrorCode.DUPLICATED_USER_NAME, String.format("UserName:%s", request.getUserName())); }); User savedUser = userRepository.save(request.toEntity(encoder.encode(request.getPassword()))); return UserDto.builder() .id(savedUser.getId()) .userName(savedUser.getUserName()) .emailAddress(savedUser.getEmailAddress()) .build(); } }
Java
복사
encode : 패스워드를 암호화해주는 메서드입니다. encde() 메서드는 SHA-1, 8바이트로 결합된 해쉬, 랜덤 하게 생성된 솔트(salt)를 지원합니다. String 반환

로그인

@AllArgsConstructor @Getter public class UserLoginRequest { private String userName; private String password; }
Java
복사
@AllArgsConstructor @Getter public class UserLoginResponse { private String token; }
Java
복사
@PostMapping("/login") public Response<UserLoginResponse> login(@RequestBody UserLoginRequest userLoginRequest) { String token = userService.login(userLoginRequest.getUserName(), userLoginRequest.getPassword()); return Response.success(new UserLoginResponse(token)); }
Java
복사
@Value("${jwt.token.secret}") private String secretKey; private long expireTimeMs = 1000 * 60 * 60; public String login(String userName, String password) { // userName있는지 여부 확인 // 없으면 NOT_FOUND에러 발생 User user = userRepository.findByUserName(userName) .orElseThrow(() -> new HospitalReviewAppException(ErrorCode.NOT_FOUND, String.format("%s는 가입된 적이 없습니다.", userName))); // password일치 하는지 여부 확인 if(!encoder.matches(password, user.getPassword())){ throw new HospitalReviewAppException(ErrorCode.INVALID_PASSWORD, String.format("userName 또는 password가 잘못 됐습니다.")); } // 두가지 확인중 예외 안났으면 Token발행 return JwtTokenUtil.createToken(userName, secretKey, expireTimeMs); }
Java
복사
matches: 제출된 인코딩 되지 않은 패스워드(일치 여부를 확인하고자 하는 패스워드)와 인코딩 된 패스워드의 일치 여부를 확인해줍니다. boolean 반환

JWT

JSON Web Token은 당사자 간의 정보를 JSON형태로 안전하게 전송하기 위한 토큰

특징

URL에서 사용할 수 있는 문자열로만 구성되어 있어 HTTP 구성요소 어디든 위치할 수 있습니다.
public class JwtTokenUtil { public static String createToken(String userName, String key, long expireTimeMs) { Claims claims = Jwts.claims(); // 일종의 map claims.put("userName", userName); return Jwts.builder() .setClaims(claims) .setIssuedAt(new Date(System.currentTimeMillis())) .setExpiration(new Date(System.currentTimeMillis() + expireTimeMs)) .signWith(SignatureAlgorithm.HS256, key) .compact(); } }
Java
복사