๋ชฉ์ฐจ
( ์ ๊ฐ ์์ ํ๋ ํ๋ก์ ํธ ์ฝ๋ ๊ธฐ๋ฐ์ผ๋ก ์ค๋ช ํ๊ณ ์์ต๋๋ค.)
1. build gradle ์ ์์กด์ฑ ์ถ๊ฐ
//์ํ๋ฆฌํฐ ๋ฒ์ ์ ์คํ๋ง๋ฒ์ ์ ๋ฐ๋ผ ์ฌ์ฉ๋ฐฉ๋ฒ์ด ์์ ํ ๋ค๋ฆ
๋๋ค. //์ํ๋ฆฌํฐ 5๋ฒ์ => ์คํ๋ง ๋ถํธ 2๋ฒ์ (์์
) //์ํ๋ฆฌํฐ 6๋ฒ์ => ์คํ๋ง ๋ถํธ 3๋ฒ์ (๋ฌธ๋ฒ์ด ์์ ๋ณ๊ฒฝ๋๋ ์ฃผ์) implementation 'org.springframework.boot:spring-boot-starter-security' //์ํ๋ฆฌํฐ ํ์๋ฆฌํ์์ ์ฌ์ฉ implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5' //์ํ๋ฆฌํฐ ํ
์คํธ testImplementation 'org.springframework.security:spring-security-test'
- ์ํ๋ฆฌํฐ๊ฐ ์ฒ์ ์ค์ ๋๋ฉด ๋ชจ๋ ์์ฒญ์ ๋ํด์ ์ํ๋ฆฌ๊ฐ ๊ธฐ๋ณธ ์ ๊ณตํ๋ ๋ก๊ทธ์ธ ํ๋ฉด์ด ๋ณด์ด๊ฒ ๋จ.
- ๊ธฐ๋ณธ์์ด๋๋ user / ๋น๋ฐ๋ฒํธ๋ console์ฐฝ์ ๋น๋ฐ๋ฒํธ๋ฅผ ๋ฐ๊ธ๋จ.
- ๋ก๊ทธ์์์ ๊ฒฝ๋ก๋ /logout์ด ๊ธฐ๋ณธ์ด ๋๋ค.
2. ์ํ๋ฆฌํฐ ์ค์ ํ์ผ ๊ตฌํ
security_config.java

์ฃผ์๋ฉ์๋

3. SecurityConfig ์ SecurityFilterChain ๊ตฌํ
package com.project.tobe.config; import com.project.tobe.dto.UserRole; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.web.SecurityFilterChain; @Configuration @EnableWebSecurity //์ํ๋ฆฌํฐ ์ค์ ํ์ผ์ ์ํ๋ฆฌํฐ ํํฐ์ ๋ฑ๋ก public class SecurityConfig { @Bean public BCryptPasswordEncoder encoder() { return new BCryptPasswordEncoder(); // ๋น๋ฐ๋ฒํธ ์ํธํ๋ฅผ ์ํ ์ธ์ฝ๋ } @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .csrf().disable()//csrfํ ํฐ ์ฌ์ฉx .authorizeRequests() // ์ธ์ฆ ๋ฐ ๊ถํ ๋ถ์ฌ ๊ท์น ์ค์ .antMatchers("/login.user").permitAll() // ๋ก๊ทธ์ธ ํ์ด์ง๋ ์ธ์ฆ ์์ด ์ ๊ทผ ํ์ฉ .antMatchers("/employee.do").hasAnyRole("S", "A") // "/employee.do" ๊ฒฝ๋ก๋ "A","S" ์ญํ ์ ๊ฐ์ง ์ฌ์ฉ์๋ง ์ ๊ทผ ๊ฐ๋ฅ .antMatchers("/*.do").authenticated() // ".do"๋ก ๋๋๋ ๋ชจ๋ ์์ฒญ์ ์ธ์ฆ๋ ์ฌ์ฉ์๋ง ์ ๊ทผ ๊ฐ๋ฅ .anyRequest().permitAll() // ๊ทธ์ธ ์์ฒญ์ ์ธ์ฆ ์์ด ์ ๊ทผ ๊ฐ๋ฅ .and() .formLogin() // ๋ก๊ทธ์ธ ์ค์ .loginPage("/login.user") // ๋ก๊ทธ์ธ ํ์ด์ง ์ค์ (์ฌ์ฉ์ ์ ์) .usernameParameter("employeeId") //๋ก๊ทธ์ธํผ์์ ์์ด๋ํ๋ name ๊ฐ์ employeeId ๋ก ์ค์ .passwordParameter("employeePw") //๋ก๊ทธ์ธํผ์์ ๋น๋ฐ๋ฒํธํ๋ name ๊ฐ์ employeePw ๋ก ์ค์ .loginProcessingUrl("/loginForm") //๋ก๊ทธ์ธ ํ์ด์ง๋ฅผ ๊ฐ๋ก์ฑ ์ํ๋ฆฌํฐ๊ฐ ์ ๊ณตํ๋ ํด๋์ค๋ก ๋ก๊ทธ์ธ์ ์ฐ๊ฒฐ .defaultSuccessUrl("/main.do") //๋ก๊ทธ์ธ ์ฑ๊ณต ์ ์ด๋ํ URL .failureUrl("/login.user?err=true") // ๋ก๊ทธ์ธ ์คํจ์ ์ด๋ URL // .failureHandler(new CustomAuthenticationFailureHandler()) // ์ปค์คํ
์คํจ ํธ๋ค๋ฌ ๋ฑ๋ก .and() .logout() // ๋ก๊ทธ์์ ์ค์ .logoutUrl("/logout") // ๋ก๊ทธ์์ ํ์ด์ง ์ค์ .logoutSuccessUrl("/login.user") // ๋ก๊ทธ์์ ์ฑ๊ณต ์ ์ด๋ํ URL .invalidateHttpSession(true)// ๋ก๊ทธ์์ ์ ์ธ์
๋ฌดํจํ .deleteCookies("JSESSIONID") // ๋ก๊ทธ์์ ์ ์ฟ ํค ์ญ์ (JSESSIONID) .and() .exceptionHandling() //์ธ์ฆ(Authentication) ๋๋ ๊ถํ ๋ถ์ฌ(Authorization) ๊ด๋ จ ์์ธ ์ฒ๋ฆฌ .accessDeniedPage("/accessDenied"); //์ ๊ทผ๊ถํ์ด ์๋ ์์ฒญ์ accessDenied ํ์ด์ง๋ก ์ด๋ return http.build(); } }

ํด๋น ํํ๋ฅผ ์ํ๋ฆฌํฐ์๊ฒ ์ ๋ฌํด์ผํ๊ธฐ ๋๋ฌธ์ ๊ฐ์ฒด ๊ตฌํ์ ๋จผ์ ํด์ค์ผํ๋ค.
4. UserDetailsService ๊ตฌํ (์ธ์ฆ๊ฐ์ฒด ๋ง๋ค๊ธฐ)

package com.project.tobe.serviceimpl; import com.project.tobe.entity.Employee; import com.project.tobe.repository.EmployeeRepository; //import com.project.tobe.security.EmployeeDetails; import com.project.tobe.security.EmployeeDetails; import lombok.RequiredArgsConstructor; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; @Service @RequiredArgsConstructor //loginProcessingUrl()๊ฐ ํธ์ถ๋๋ฉด loadUserByUsername์ด ์๋ ํธ์ถ๋จ public class PrincipalDetailsService implements UserDetailsService { private final EmployeeRepository employeeRepository; /** * ์ฌ์ฉ์์ ์์ด๋(username)๋ฅผ ํตํด ์ธ์ฆ ์ ๋ณด๋ฅผ ๋ก๋ํ๋ ๋ฉ์๋์
๋๋ค. * ์คํ๋ง ์ํ๋ฆฌํฐ์์ ๋ก๊ทธ์ธ ์์ฒญ ์ ํธ์ถ๋ฉ๋๋ค. */ @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { // ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ username์ ๊ธฐ์ค์ผ๋ก Employee ๊ฐ์ฒด ์กฐํ Employee employee = employeeRepository.findById(username) .orElseThrow(() -> { // username ์ ํด๋นํ๋ ์ฌ์ฉ์๊ฐ ์์ผ๋ฉด ์์ธ ๋ฐ์ return new UsernameNotFoundException("ํด๋น ์ ์ ๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค."); }); // ์กฐํ๋ ์ฌ์ฉ์ ๊ฐ์ฒด๋ฅผ EmployeeDetails (์ ์ ๊ฐ์ฒด) ๋ก ๋ณํํ์ฌ ๋ฐํ // ์ํ๋ฆฌํฐ์ธ์
( new Authentication(new MyUserDetails()) ) ํ์์ผ๋ก ์ ๋ฌ ๋จ return new EmployeeDetails(employee); } }
5.UserDetails ๊ตฌํ (์ ์ ๊ฐ์ฒด ๋ง๋ค๊ธฐ)

package com.project.tobe.security; import com.project.tobe.dto.EmployeeDTO; import com.project.tobe.entity.Employee; import com.project.tobe.dto.UserRole; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import java.util.ArrayList; import java.util.Collection; public class EmployeeDetails implements UserDetails { private Employee employee; private EmployeeDTO employeedto; public EmployeeDetails(Employee employee) { this.employee = employee; } // ๊ถํ ๊ด๋ จ ์์
์ ํ๊ธฐ ์ํ role return @Override public Collection<? extends GrantedAuthority> getAuthorities() { Collection<GrantedAuthority> authorities = new ArrayList<>(); try { UserRole role = UserRole.valueOf(employee.getAuthorityGrade()); // authorityGrade๋ฅผ UserRole Enum์ผ๋ก ๋ณํ authorities.add(new SimpleGrantedAuthority("ROLE_" + role.name())); // ROLE_ ์ ๋์ฌ ๋ถ์ด๊ธฐ } catch (IllegalArgumentException e) { // authorityGrade ๊ฐ์ด UserRole Enum์ ์ ์๋์ด ์์ง ์์ ๊ฒฝ์ฐ์ ์ฒ๋ฆฌ System.err.println("Invalid authorityGrade value: " + employee.getAuthorityGrade()); } return authorities; } // get Password ๋ฉ์๋ @Override public String getPassword() { return employee.getEmployeePw(); } // get Username ๋ฉ์๋ (์์ฑํ User์ loginId ์ฌ์ฉ) @Override public String getUsername() { return employee.getEmployeeId(); } public String getNickname() { System.out.println(employee.toString()); System.out.println(employee.getEmployeeName()); return employee.getEmployeeName(); } public String getUserAuthorityGrade() { return employee.getAuthorityGrade(); } // public String getUserAuthorityName() { // System.out.println(employeedto.getAuthorityName()); // return employeedto.getAuthorityName(); // } // ๊ณ์ ์ด ๋ง๋ฃ ๋์๋์ง (true: ๋ง๋ฃX) @Override public boolean isAccountNonExpired() { return true; } // ๊ณ์ ์ด ์ ๊ฒผ๋์ง (true: ์ ๊ธฐ์ง ์์) @Override public boolean isAccountNonLocked() { return true; } // ๋น๋ฐ๋ฒํธ๊ฐ ๋ง๋ฃ๋์๋์ง (true: ๋ง๋ฃX) @Override public boolean isCredentialsNonExpired() { return true; } // ๊ณ์ ์ด ํ์ฑํ(์ฌ์ฉ๊ฐ๋ฅ)์ธ์ง (true: ํ์ฑํ) @Override public boolean isEnabled() { return true; } }
6. ์ธ์ฆ ๊ฐ์ฒด ์ ๋ณด ๊ฐ์ ธ์ค๊ธฐ
์ปจํธ๋กค๋ฌ
//ํ์ฌ ๋ก๊ทธ์ธ ์ ๋ณด ๊ฐ์ ธ์ค๊ธฐ @GetMapping("/user-info") public ResponseEntity<?> employeeUserInfo(Authentication authentication) { if (authentication == null) { // ์ธ์ฆ ๊ฐ์ฒด๊ฐ ์๋ค๋ฉด return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Unauthorized"); } //์ธ์ฆ๊ฐ์ฒด ์์ principal๊ฐ์ ์ป์ผ๋ฉด ์ ์ ๊ฐ์ฒด๊ฐ ๋์ด EmployeeDetails user = (EmployeeDetails)authentication.getPrincipal(); System.out.println("๋ก๊ทธ์ธ ์ ๋ณด (์ด๋ฆ):" + user.getNickname()); System.out.println("๋ก๊ทธ์ธ ์ ๋ณด (์์ด๋):" + user.getUsername()); System.out.println("๋ก๊ทธ์ธ ์ ๋ณด (๋น๋ฐ๋ฒํธ) :" + user.getPassword()); System.out.println("๋ก๊ทธ์ธ ์ ๋ณด (๊ถํ) :" + user.getUserAuthorityGrade()); // ์ฌ์ฉ์ ์ด๋ฆ๊ณผ ๊ถํ์ ๋ฐํ Map<String, Object> response = new HashMap<>(); response.put("userName", user.getNickname()); response.put("userId", user.getUsername()); response.put("userPw", user.getPassword()); response.put("userAuthorityGrade", user.getUserAuthorityGrade()); return ResponseEntity.ok(response); }

view (ํ์๋ฆฌํ)
<html xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/extras/spring-security"> . . . <!-- ์ธ์ฆ ๋์๋ค๋ฉด true, ์๋๋ฉด false๋ฅผ ๋ฐํํฉ๋๋ค. if๋ฌธ์ฒ๋ผ ์ฌ์ฉ๋ฉ๋๋ค. --> <div sec:authorize="isAuthenticated()"> <span>[[${#authentication.principal.getUsername}]]</span><br/> <span>[[${#authentication.principal.getNickname}]]</span><br/> <span>[[${#authentication.principal.getUserAuthorityGrade}]]</span><br/> </div>

view (๋ฆฌ์กํธ)
useEffect(() => { const fetchUserId = async () => { try { const response = await axios.get('/employee/user-info'); console.log("์ธ์ฆ๋ ์ฌ์ฉ์ ์ ๋ณด" + response.data); } catch (error) { console.error("Error fetching user ID:", error); } }; fetchUserId(); }, []);

7. ์ธ์ - ์ฟ ํค ๋ฐฉ์
