Entity
@Data
@Entity
@Table(name="member")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq")
@SequenceGenerator(name = "seq", sequenceName = "seq", allocationSize = 1)
private Long id;
private String username;
private String name;
private String password;
private String role;
private LocalDateTime regdate;
}
UserDetails
public class SecurityUserDetails implements UserDetails {
private final User user;
public SecurityUserDetails(User user) { this.user = user;}
public Member getMember() {return user;}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority(user.getRole()));
return authorities;
}
@Override
public String getPassword() {
return user.getPassword();
}
@Override
public String getUsername() {
return user.getUsername();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
UserDetailService
@Service
public class SecurityUserDetailsService implements UserDetailsService {
@Autowired
private UserService userService;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userService.findByUsername(username); // DB에서 사용자 정보 가져오기
if (user == null) {
throw new UsernameNotFoundException("User not found.");
}
// Spring Security UserDetails 객체 반환
return new SecurityUserDetails(user);
}
}
AuthenticationProvider
@Component
public class SecurityProvider implements AuthenticationProvider {
@Autowired
private SecurityUserDetailsService userDetailsService;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String username = authentication.getName();
String password = (String) authentication.getCredentials();
SecurityUserDetails userDetails = (SecurityUserDetails)userDetailsService.loadUserByUsername(username);
String dbPassword = userDetails.getPassword();
PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
if(!passwordEncoder.matches(password, dbPassword)) {
System.out.println("[사용자] 비밀번호가 일치하지 않습니다.");
throw new BadCredentialsException("[사용자] 아이디 또는 비밀번호가 일치하지 않습니다.");
}
User member = userDetails.getMember();
if (member == null || "N".equals(member.getUsername())) {
System.out.println("[사용자] 사용할 수 없는 계정입니다.");
throw new BadCredentialsException("[사용자] 사용할 수 없는 계정입니다.");
}
// 인증이 성공하면 UsernamePasswordAuthenticationToken 객체를 반환한다.
// 해당 객체는 Authentication 객체로 추후 인증이 끝나고 SecurityContextHolder.getContext() 에 저장된다.
return new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
}
@Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}
SecurityConifg (SecurityFilterChain)
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Autowired
SecurityUserDetailsService securityUserDetail;
@Autowired
SecurityProvider securityProvider;
@Autowired
public void configure (AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(securityProvider);
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.ALWAYS) // 세션이 필요할 때 생성
.maximumSessions(1) // 최대 세션 수
.expiredUrl("/member/login") // 세션 만료 시 이동할 URL
)
//권한설정
.securityMatcher("/api/**","/user/**","/manager/**","/admin/**")
.authorizeHttpRequests(authorize ->
authorize
.requestMatchers("/api/*", "/member/login/**", "/error").permitAll() //해당 경로는 권한 X
//앞에 ROLE_ 이 무조건 있어야 됨 ROLE_USER , ROLE_MANAGER 등
.requestMatchers("/user/**").hasRole("USER") // USER 권한을 가진 아디디만
.requestMatchers("/manager/**").hasRole("MANAGER") // MANAGER 권한을 가진 아디디만
.requestMatchers("/admin/**").hasRole("ADMIN") // ADMIN 유저 권한을 가진 아디디만
)
//로그인
.formLogin(form ->
form
.loginPage("/member/login/loginForm") // 로그인 페이지 설정
.loginProcessingUrl("/member/login/login") // 로그인 처리 URL 설정 Controller 설정 X
// .defaultSuccessUrl("/api/board") // 로그인 성공 후 이동할 페이지
.successHandler(new MemberAuthSuccessHandler()) // 로그인 성공 후 처리할 핸들러
.failureHandler(new MemberAuthFailureHandler())// 로그인 실패 후 처리할 핸들러
.permitAll()
)
//로그아웃
.logout(logout ->
logout
.logoutUrl("/member/login/logout") // 로그아웃 처리 URL 설정 Controller 설정 X
.logoutSuccessUrl("/member/login/loginForm?logout=1") // 로그아웃 성공 후 이동할 페이지
.deleteCookies("JSESSIONID") // 로그아웃 후 쿠키 삭제
)
//자동 로그인 7일 동안
.rememberMe(remember ->
remember
.key("jj") // 인증 토큰 생성시 사용할 키
.tokenValiditySeconds(60 * 60 * 24 * 7) // 인증 토큰 유효 시간 (초)
.userDetailsService(securityUserDetail) // 인증 토큰 생성시 사용할 UserDetailsService
//로그인 페이지에 form에 <input type="checkbox" name="remember-me" id="rememberMe"> 이런식으로 사용
.rememberMeParameter("remember-me") // 로그인 페이지에서 사용할 파라미터 이름
)
//csrf 해제
.csrf(AbstractHttpConfigurer::disable);
return http.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
SuccessHandler
public class MemberAuthSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
// 로그인 성공시 이동할 페이지
setDefaultTargetUrl("/api/board");
// 로그인 성공시 이동할 페이지로 이동
super.onAuthenticationSuccess(request, response, authentication);
}
}
FailHandler
public class MemberAuthFailureHandler extends SimpleUrlAuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
// 실패 메세지를 담기 위한 세션 선언
HttpSession session = request.getSession();
// 세션에 실패 메세지 담기
session.setAttribute("loginErrorMessage", exception.getMessage());
// 로그인 실패시 이동할 페이지
setDefaultFailureUrl("member/login?error=true&t=h");
// 로그인 실패시 이동할 페이지로 이동
super.onAuthenticationFailure(request, response, exception);
}
}
'Framework > Spring' 카테고리의 다른 글
[PageHelper] 페이지 헬퍼 네비게이션 및 유틸 (1) | 2024.04.26 |
---|---|
[setting] spring banner 바꾸는 법 (0) | 2023.10.13 |
[setting] lucy-xss 설정 (0) | 2023.10.13 |
[setting] logback 설정 (0) | 2023.10.13 |
[Java] mybatis mapping (0) | 2023.10.13 |