반응형
Spring Security
: 스프링 시큐리티는 스프링 기반의 애플리케이션의 보안을 담당하는 스프링 하위 프레임워크이다.
주로 필터에서 돌아간다.
즉, Security는 보안 체계를 강화하기 위해 사용한다.
보안 용어
- 인증 (Authentication)
: 보호된 리소스에 접근 대상에 대해 이 유저가 누구인지, 애플리케이션의 작업을 수행해도 되는 주체인지 확인하는 과정 - 인가 (Authorize)
: 해당 리소스에 대해 접근 가능한 권한을 가지고 있는지 확인하는 과정
Security를 사용하기 위해서 build.gradle에서 의존성 추가하기
// security
// https://mvnrepository.com/artifact/org.springframework.security/spring-security-taglibs
implementation 'org.springframework.security:spring-security-taglibs:5.6.2'
https://www.baeldung.com/spring-security-taglibs
→ 사용 태그 라이브러리 선언
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>
→ ‘인증된 사용자라면‘ 이라는 뜻
<sec:authorize access="isAuthenticated()">
Welcome Back, <sec:authentication property="name"/>
</sec:authorize>
시큐리티 주소 설계 → 권한 부여 페이지 앞에는 /auth 붙일 예정
auth 패키지
- UserDetails
: Spring Security에서 사용자의 정보를 담는 인터페이스 - UserDetailService
: Spring Security에서 유저의 정보를 가져오는 인터페이스
auth/PrincipalDetail.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | public class PrincipalDetail implements UserDetails { // User private User user; public PrincipalDetail(User user) { this.user = user; } // 계정의 권한을 반환한다. @Override public Collection<? extends GrantedAuthority> getAuthorities() { // "ROLE_" 스프링 시큐리티 규칙 (꼭 넣어야 함!) // "ROLE_" + user.getRole(); Collection<GrantedAuthority> collections = new ArrayList<GrantedAuthority>(); collections.add(() -> { return "ROLE_" + user.getRole(); }); return collections; } @Override public String getPassword() { return user.getPassword(); } @Override public String getUsername() { return user.getUsername(); } // 계정 만료되지 않았는지 여부를 리턴 / true : 게정 만료 안 됨, false : 계정 만료 됨 @Override public boolean isAccountNonExpired() { return true; } // 계정 잠김 여부 확인 / true : 사용 가능, flase : 사용 불가능 @Override public boolean isAccountNonLocked() { return true; } // 비밀번호 만료 여부를 리턴 / true : 사용 가능, flase : 사용 불가능 @Override public boolean isCredentialsNonExpired() { return true; } // 계정 활성화 여부 / true : 사용 가능, false : 로그인 불가능 @Override public boolean isEnabled() { return true; } } | cs |
auth/PrincipalDetailService.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | @Service public class PrincipalDetailService implements UserDetailsService { @Autowired private UesrRepository userRepository; @Override public UserDetails loadUserByUsername(String username) throw UsernameNotFoundException { // 유저 이름, 비밀번호 같이 받게 설계 // !! 시큐리티는 비밀번호 확인하지 않음. -> DB 사용자 계정이 있는지만 먼저 검사한다. User principal = userRepository.findByUsername(username).orElseThrow(() -> { return new UsernameNotFoundException("해당 유저를 찾을 수 없습니다."); }); // 시큐리티 세션 영역에 유저 정보가 저장 return new PrincipalDetail(principal); } } | cs |
Config패키지
- WebSecurityConfigurerAdapter
: 스프링 시큐리티의 웹 보안 기능의 초기화 및 설정을 담당한다.
SecurityConfig.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | /* 권한 부여 페이지 설정 1. /auth/joinPage 2. /auth/loginPage 3. /auth/** */ @Configuration // IoC 등록 @EnableWebSecurity // Security 필터로 등록을 해라 (+ 필터 커스텀) @EnableGlobalMethodSecurity(prePostEnabled = true) // 특정 주소로 접근하면 권한 및 인증 처리를 미리 하겠다. public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private PrincipalDetailService principalDetailService; @Bean public BCryptPasswordEncoder encodedPwd() { return new BCryptPasswordEncoder(); } // 특정 주소 필터를 설정할 예정 @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable(); // csrf 토큰 비활성화 처리 (테스트시 사용 권장) http.authorizeHttpRequests() .antMatchers("/auth/**", "/", "/js/**", "/css/**", "/images/**") .permitAll() .anyRequest() .authenticated() .and().formLogin() .loginPage("/auth/loginPage") // 시큐리티 로그인 페이지를 우리가 만든 페이지로 커스텀 .loginProcessingUrl("/auth/loginProc") .defaultSuccessUrl("/"); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(principalDetailService).passwordEncoder(encodePwd()); } } | cs |
UserService.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | @Service public class UserService { @Autowired private UserRepository userRepository; @Autowired private BCryptPasswordEncoder encoder; @Transactional public int createUser(User user) { try { String rawPwd = user.getPassword(); String encPwd = encoder.encode(rawPwd); user.setRole("user"); user.setPassword(encPwd); userRepository.save(user); return 1; } catch (Exception e) { e.printStackTrace(); } return -1; } } | cs |
UserApiController.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | @RestController public class UserApiController { @Autowired private UserService userService; @Autowired private HttpSession session; // 회원가입 처리 @PostMapping("/api/user") public ResponseDto<Integer> saveUser(@RequestBody User user) { int result = userService.createUser(user); return new ResponseDto<>(HttpStatus.OK, result); } } | cs |
header.jsp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags"%> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>그린스블로그</title> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/css/bootstrap.min.css"> <script src="https://cdn.jsdelivr.net/npm/jquery@3.6.4/dist/jquery.slim.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/js/bootstrap.bundle.min.js"></script> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.4/jquery.min.js"></script> </head> <body> <sec:authorize access="isAuthenticated()"> Welcome Back, <sec:authentication property="name" /> Welcome Back, <sec:authentication property="principal" var="principal"/> </sec:authorize> <nav class="navbar navbar-expand-md bg-dark navbar-dark"> <a class="navbar-brand" href="/">Home</a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#collapsibleNavbar"> <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse" id="collapsibleNavbar"> <ul class="navbar-nav"> <c:choose> <%-- <c:when test="${principal != null}"> --%> <c:when test="${empty principal}"> <li class="nav-item"><a class="nav-link" href="/auth/loginPage">로그인</a></li> <li class="nav-item"><a class="nav-link" href="/auth/joinPage">회원가입</a></li> </c:when> <c:otherwise> <li class="nav-item"><a class="nav-link" href="#">글쓰기</a></li> <li class="nav-item"><a class="nav-link" href="#">회원정보</a></li> <li class="nav-item"><a class="nav-link" href="/logout">로그아웃</a></li> </c:otherwise> </c:choose> </ul> </div> </nav> <br> | cs |
반응형
'프로그래밍 > Security' 카테고리의 다른 글
Security_시큐리티 동작 방식 (0) | 2023.07.16 |
---|