spring security JDBC를 이용해 간단한 로그인을 구현해봅니다.
저는 아래의 것을 사용합니다.
Gradle
, lombok
, MySql
데이터베이스 설계와 테이블 DTO 작성이 필요합니다.
데이터베이스 정보를 이용해 로그인 하기 위해서는 우선 테이블
을 설계 해야합니다.
간단히 사용자 user
테이블과 권한 role
테이블이 필요하며, 이 두 테이블은 다대다 관계
를 가집니다.
따라서 연결 테이블 user_role
을 사용합니다.
user
테이블의 username
이 로그인 아이디가 됩니다.
로그인을 위해 임의의 데이터를 넣어두겠습니다.
username은 test
, password는 1234
를 암호화한 값을 넣어줍니다.
INSERT INTO user(id, username, password, enabled)
VALUES(1, 'test', '$2a$10$PSVJfM/zxo4.CHq5QZHWZulFo/dT0SSiUxUNOWu1.lypNG16h0.8C', 1);
INSERT INTO role(id, authority) VALUES(1, 'ROLE_USER');
INSERT INTO user_role(user_id, role_id) VALUES(1, 1);
사용자가 test
/ 1234
로 로그인할 수 있으며, ROLE_USER
권한을 갖고 있습니다.
user
테이블과 role
테이블에 대한 DTO를 작성합니다.
이때 @ManyToMany
, @JoinTable
어노테이션을 이용해 다대다 관계와 연결 테이블을 지정해줍니다.
@Data
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
private String password;
private Boolean enabled;
@ManyToMany
@JoinTable(
name = "user_role",
joinColumns = @JoinColumn(name = "user_id"),
inverseJoinColumns = @JoinColumn(name = "role_id"))
private List<Role> roles = new ArrayList<>();
}
@Data
@Entity
public class Role {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String authority;
@ManyToMany(mappedBy = "roles")
private List<User> users;
}
본격적으로 spring security를 구현해보겠습니다.
먼저 의존성을 추가해 줍니다.
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.security:spring-security-test'
스프링 시큐리티 Config 클래스는 WebSecurityConfigurerAdapter
를 상속받으며
@EnableWebSecurity
어노테이션을 추가합니다.
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/", "/register").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll();
}
}
스프링 시큐리티 설정은 HttpSecurity
로 합니다.
.authorizeRequests()
는 리소스의 권한을 설정합니다.
.antMatchers("/", "/register").permitAll()
: /
또는 /register
에 대한 접근은 모두 허용합니다.
.anyRequest().authenticated()
: 그 외 나머지 리소스는 인증된 사용자만 접근할 수 있습니다.
.formLogin()
는 로그인 처리를 설정합니다.
로그인 form 페이지를 이용하여 로그인하는 방식입니다.
.loginPage("/login")
: 로그인 페이지를 지정합니다. 사용자가 만든 로그인 페이지를 사용하려 할 때 설정하며, 설정하지 않으면 스프링이 제공하는 기본 로그인 페이지가 호출됩니다.
로그인 성공 시 이동할 페이지, 실패 시 이동할 페이지 등도 지정할 수 있습니다.
.logout()
은 로그아웃 처리를 설정합니다.
AuthenticationManagerBuilder
를 이용해 로그인 인증을 처리합니다.
@Autowired
private DataSource dataSource;
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth)
throws Exception {
auth.jdbcAuthentication()
.dataSource(dataSource)
.passwordEncoder(passwordEncoder())
.usersByUsernameQuery("select username, password, enabled "
+ "from user "
+ "where username = ?")
.authoritiesByUsernameQuery("select u.username, r.authority "
+ "from user_role ur inner join user u on ur.user_id = u.id "
+ "inner join role r on ur.role_id = r.id "
+ "where u.username = ?");
}
@Bean
public static PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
auth.jdbcAuthentication()
: DB 기반의 인증을 처리하도록 합니다.
.dataSource(dataSource)
: dataSource를 지정해줍니다.
.passwordEncoder(passwordEncoder())
: 패스워드 암호화를 적용합니다.
.usersByUsernameQuery()
: 사용자 목록을 가져오는 쿼리를 작성합니다. 출력 결과는 사용자이름
, 비밀번호
, enabled
로 총 3개여야 합니다.
.authoritiesByUsernameQuery()
: 권한을 불러오는 쿼리를 작성합니다. 출력 결과는 사용자이름
, 권한명
으로 총 2개여야 합니다.
로그인 하지 않은 상태에서 /board/list
로 접속하면 로그인 페이지로 이동됩니다.
로그인 한 뒤 접속할 수 있습니다.