SecurityConfig.java
package com.chatting.config;
import com.chatting.common.Constants;
import com.chatting.common.Url;
import com.chatting.service.CustomUsersDetailService;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import javax.sql.DataSource;
/**
* Security 설정 클래스
*/
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final LoginSucessHandler loginSucessHandler;
private final LoginFailureHandler loginFailureHandler;
private final DataSource dataSource;
private final CustomUsersDetailService customUsersDetailService;
/* configure */
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(new FrontAuthenticationProvider());
super.configure(auth);
}
/**
* http 요청검사
* @param http
* @throws Exception
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.rememberMe()
.rememberMeParameter("remember-me")
.userDetailsService(customUsersDetailService)
.tokenRepository(tokenRepository()) //DataSource 추가
.and()
.csrf().disable()
.authorizeRequests()
.antMatchers("/file-download/**").permitAll() //파일 다운로드
.antMatchers("/auth/**").permitAll() //로그인, 회원가입 접속허용
.antMatchers("/resource/**/images/**").permitAll() //이미지
.anyRequest().authenticated()
.and()
//로그인 화면 설정
.formLogin()
.permitAll()
.loginPage(Url.AUTH.LOGIN)
.loginProcessingUrl(Url.AUTH.LOGIN_PROC)
.successHandler(loginSucessHandler)
.failureHandler(loginFailureHandler)
.usernameParameter(USERNAME_PARAM)
.passwordParameter(PASSWORD_PARAM)
.and()
.logout()
.logoutUrl(Url.AUTH.LOGOUT_PROC)
.invalidateHttpSession(true)
.deleteCookies("JSESSIONID","remember-me")//로그아웃 후 쿠키 삭제
.and()
//세션관리
.sessionManagement()
.maximumSessions(200) //세션 허용 갯수
.expiredUrl(Url.AUTH.LOGIN) //세션 만료시 이동할 페이지
.sessionRegistry(sesionRegistry())
.maxSessionsPreventsLogin(true); //동시 로그인 차단, false인 경우 기존 세션 만료
}
@Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers(Constants.STATIC_RESOURCES_URL_PATTERS)
.antMatchers(HttpMethod.GET, "/exception/**");
super.configure(web);
}
/**
* 패스워드 암호화
* @return
*/
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public SessionRegistry sesionRegistry() {
return new SpringSecuritySessionRegistImpl();
}
@Bean
public PersistentTokenRepository tokenRepository() {
// JDBC 기반의 tokenRepository 구현체
JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
jdbcTokenRepository.setDataSource(dataSource); // dataSource 주입
return jdbcTokenRepository;
}
/* 관리자 아이디 파라미터 이름 */
public static final String USERNAME_PARAM = "userId";
/* 관리자 비밀번호 파라미터 이름 */
public static final String PASSWORD_PARAM = "password";
}
customDetailService.java
package com.chatting.service;
import com.chatting.dto.QuickGuideUser;
import com.chatting.entity.Users;
import com.chatting.entity.UsersAuthority;
import com.chatting.repository.UsersAuthorityRepository;
import com.chatting.repository.UsersRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
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;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@Slf4j
@RequiredArgsConstructor
@Service
public class CustomUsersDetailService implements UserDetailsService{
private final UsersRepository usersRepository;
private final UsersAuthorityRepository usersAuthorityRepository;
@Override
public UserDetails loadUserByUsername(String userId) throws UsernameNotFoundException {
Users user = usersRepository.findByUserId(userId);
if (user == null){
throw new UsernameNotFoundException(userId + "is not found. ");
}
QuickGuideUser quickGuideUser = new QuickGuideUser();
quickGuideUser.setUsername(user.getUsername());
quickGuideUser.setPassword(user.getPassword());
quickGuideUser.setAuthorities(getAuthorities(userId));
quickGuideUser.setEnabled(true);
quickGuideUser.setAccountNonExpired(true);
quickGuideUser.setAccountNonLocked(true);
quickGuideUser.setCredentialsNonExpired(true);
return quickGuideUser;
}
public Collection<GrantedAuthority> getAuthorities(String username) {
List<UsersAuthority> authList = usersAuthorityRepository.findByUserId(username);
List<GrantedAuthority> authorities = new ArrayList<>();
for (UsersAuthority authority : authList) {
authorities.add(new SimpleGrantedAuthority(authority.getAuthority()));
}
return authorities;
}
}
QuickGuideUser.java
package com.chatting.dto;
import lombok.Data;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
@Data
public class QuickGuideUser implements UserDetails {
private String username;
private String password;
private boolean isEnabled;
private boolean isAccountNonExpired;
private boolean isAccountNonLocked;
private boolean isCredentialsNonExpired;
private Collection<? extends GrantedAuthority> authorities;
}
UsersRepository.java
package com.chatting.repository;
import com.chatting.dto.UsersDto;
import com.chatting.entity.Users;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface UsersRepository extends CrudRepository<Users, Long> {
//이름으로 찾기
Users findByUserId(String userId);
}
UsersAuthorityRepository.java
package com.chatting.repository;
import com.chatting.dto.UsersAuthorityDto;
import com.chatting.entity.Users;
import com.chatting.entity.UsersAuthority;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface UsersAuthorityRepository extends CrudRepository<UsersAuthority, Long> {
//아이디로 검색
List<UsersAuthority> findByUserId(String userId);
}
Users.java
package com.chatting.entity;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import javax.persistence.*;
import java.util.Collection;
import java.util.List;
@Getter
@Setter
@Entity
public class Users implements UserDetails {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long userIdx;
//사용자 아이디
private String userId;
//사용자 패스워드
private String password;
//사용자 핸드폰번호
private String handPhoneNo;
private String nickName;
//사용여부
private String useYn;
//푸시토큰
private String token;
public Users() {}
@Transient
public List<UsersAuthority> authorities;
@Builder
public Users(String userId, String password, String handPhoneNo, String nickName, String useYn, String token) {
this.userId = userId;
this.password = password;
this.handPhoneNo = handPhoneNo;
this.nickName = nickName;
this.useYn = useYn;
this.token = token;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
@Override
public String getUsername() {
return userId;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
UsersAuthority.java
package com.chatting.entity;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import org.springframework.security.core.GrantedAuthority;
import javax.persistence.*;
@Entity
@Getter
@Setter
public class UsersAuthority implements GrantedAuthority{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long authIdx;
//사용자 id
@Column(columnDefinition = "varchar(45) not null comment '사용자 아이디'")
private String userId;
//사용자 권한
@Column(columnDefinition = "varchar(45) not null comment '권한'")
private String authority;
public UsersAuthority() {}
@Builder
public UsersAuthority(String userId, String authority) {
this.userId = userId;
this.authority = authority;
}
}
PersistentLogins.java
package com.chatting.entity;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import java.time.LocalDateTime;
@Table(name = "persistent_logins")
@Entity
@Getter
@Setter
public class PersistentLogins {
@Id
@Column(length = 64)
private String series;
@Column(nullable = false, length = 64)
private String username;
@Column(nullable = false, length = 64)
private String token;
@Column(name = "last_used", nullable = false, length = 64)
private LocalDateTime lastUsed;
}
login.html
<div class="form-group form-check">
<input type="checkbox" class="form-check-input" id="rememberMe" name="remember-me" checked>
<label class="form-check-label" for="rememberMe" aria-describedby="rememberMeHelp">로그인 유지</label>
</div>
쿠키에 remember-me라는 항목이 생긴것을 볼수있습니다.
persistent_logins 테이블에 로그인 쿠키 데이터가 저장된것을 볼수 있습니다.
'스프링' 카테고리의 다른 글
[Spring] json, xml 데이터 return 하기 (0) | 2021.11.23 |
---|---|
[Spring Boot] 이메일 발송하기 (Java mail) (0) | 2021.10.26 |
[JPA] @valid 어노테이션을 사용하여 벨리데이션 처리하기 (0) | 2021.10.18 |
[SpringBoot] Querydsl 적용 (1) | 2021.10.06 |
[Spring boot] 웹서비스 ec2에 배포하기 (RDS 데이터베이스 연동) (0) | 2021.10.06 |