Search

์ž๋™ ๋กœ๊ทธ์ธ

๋กœ๊ทธ์ธ

Spring Security 6

์ด์ „ ํŽ˜์ด์ง€

์ด์ „ ํŽ˜์ด์ง€ ๋‚ด์šฉ์— ์ด์–ด์„œ ์ง„ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

Code

Preview

1.
๋กœ๊ทธ์ธ ํ™”๋ฉด

์ž‘์—… ํ”„๋กœ์„ธ์Šค

1.
ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ
2.
์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ ์„ค์ •
3.
์š”์ฒญ ๊ฒฝ๋กœ ๋งคํ•‘

Preview

๋กœ๊ทธ์ธ ํ™”๋ฉด

์ž๋™ ๋กœ๊ทธ์ธ ์ฒดํฌ๋ฐ•์Šค๊ฐ€ ์ถ”๊ฐ€๋˜์—ˆ์Šต๋‹ˆ๋‹ค!
์ž๋™ ๋กœ๊ทธ์ธ ์ฒดํฌ ํ›„, ๋ธŒ๋ผ์šฐ์ €๋ฅผ ์ข…๋ฃŒ ํ›„ ์žฌ์‹คํ–‰ํ•˜์—ฌ๋„ ๋กœ๊ทธ์ธ์ด ๋˜์–ด ์žˆ๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ฉ”์ธ ํ™”๋ฉด

์ž‘์—… ํ”„๋กœ์„ธ์Šค

1.
ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ
2.
์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ ์„ค์ •

ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ

build.gradle

spring boot 3.x.x
spring security 6.x.x
plugins { id 'java' id 'war' id 'org.springframework.boot' version '3.3.5' id 'io.spring.dependency-management' version '1.1.6' } group = 'com.aloha' version = '0.0.1-SNAPSHOT' java { toolchain { languageVersion = JavaLanguageVersion.of(17) } } configurations { compileOnly { extendsFrom annotationProcessor } } repositories { mavenCentral() } dependencies { implementation 'org.springframework.boot:spring-boot-starter-security' implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:3.0.3' implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6' compileOnly 'org.projectlombok:lombok' developmentOnly 'org.springframework.boot:spring-boot-devtools' runtimeOnly 'com.mysql:mysql-connector-j' annotationProcessor 'org.projectlombok:lombok' providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat' testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter-test:3.0.3' testImplementation 'org.springframework.security:spring-security-test' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' } tasks.named('test') { useJUnitPlatform() }
Java
๋ณต์‚ฌ

์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ ์„ค์ •

์ž๋™ ๋กœ๊ทธ์ธ ์„ค์ •
http.rememberMe(me -> me.key("aloha") .tokenRepository(tokenRepository()) .tokenValiditySeconds(60 * 60 * 24 * 7));
Java
๋ณต์‚ฌ
์ž๋™ ๋กœ๊ทธ์ธ ์ €์žฅ์†Œ ๋นˆ ๋“ฑ๋ก
โ€ข
persistent_logins ํ…Œ์ด๋ธ”๋กœ ์ž๋™ ๋กœ๊ทธ์ธ ์ •๋ณด(์•„์ด๋””, ์‹œ๋ฆฌ์ฆˆ, ํ† ํฐ)๋ฅผ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
โ€ข
์ง์ ‘ DB์— ํ…Œ์ด๋ธ”์„ ์ƒ์„ฑํ•˜์—ฌ๋„ ์ข‹๊ณ , ์•„๋ž˜ ์ฝ”๋“œ์—์„œ๋Š” ์ž๋™์œผ๋กœ ์ƒ์„ฑ๋˜๋„๋ก ๋ฉ”์†Œ๋“œ๋ฅผ ์ •์˜ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

persistent_logins

create table persistent_logins ( username varchar(64) not null , series varchar(64) primary key , token varchar(64) not null , last_used timestamp not null );
SQL
๋ณต์‚ฌ

persistent_logins ํ…Œ์ด๋ธ” ์ž๋™ ์ƒ์„ฑ ์ฝ”๋“œ

repositoryImpl.getJdbcTemplate().execute(JdbcTokenRepositoryImpl.CREATE_TABLE_SQL);
Java
๋ณต์‚ฌ
์ด ์ฝ”๋“œ๋ฅผ ํ†ตํ•ด์„œ JdbcTokenRepositoryImpl ๊ฐ์ฒด์— ๋ฏธ๋ฆฌ ์ •์˜๋œ CREATE_TABLE_SQL ๋ฅผ ์‹คํ–‰ํ•˜์—ฌ persistent_logins ์„ ์ž๋™์œผ๋กœ ์ƒ์„ฑ๋˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ์กด์— ํ…Œ์ด๋ธ”์˜ ์ƒ์„ฑ๋˜์–ด ์žˆ์œผ๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ, ์ด๋ฅผ ์˜ˆ์™ธ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
/** * ๐Ÿƒ ์ž๋™ ๋กœ๊ทธ์ธ ์ €์žฅ์†Œ ๋นˆ ๋“ฑ๋ก * โœ… ๋ฐ์ดํ„ฐ ์†Œ์Šค * โญ persistent_logins ํ…Œ์ด๋ธ” ์ƒ์„ฑ create table persistent_logins ( username varchar(64) not null , series varchar(64) primary key , token varchar(64) not null , last_used timestamp not null ); * ๐Ÿ”„ ์ž๋™ ๋กœ๊ทธ์ธ ํ”„๋กœ์„ธ์Šค * โœ… ๋กœ๊ทธ์ธ ์‹œ * โžก ๐Ÿ‘ฉโ€๐Ÿ’ผ(ID, ์‹œ๋ฆฌ์ฆˆ, ํ† ํฐ) ์ €์žฅ * โœ… ๋กœ๊ทธ์•„์›ƒ ์‹œ, * โžก ๐Ÿ‘ฉโ€๐Ÿ’ผ(ID, ์‹œ๋ฆฌ์ฆˆ, ํ† ํฐ) ์‚ญ์ œ * @return */ @Bean public PersistentTokenRepository tokenRepository() { // JdbcTokenRepositoryImpl : ํ† ํฐ ์ €์žฅ ๋ฐ์ดํ„ฐ ๋ฒ ์ด์Šค๋ฅผ ๋“ฑ๋กํ•˜๋Š” ๊ฐ์ฒด JdbcTokenRepositoryImpl repositoryImpl = new JdbcTokenRepositoryImpl(); // โœ… ํ† ํฐ ์ €์žฅ์†Œ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐ์ดํ„ฐ ์†Œ์Šค ์ง€์ • // - ์‹œํ๋ฆฌํ‹ฐ๊ฐ€ ์ž๋™ ๋กœ๊ทธ์ธ ํ”„๋กœ์„ธ์Šค๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ DB๋ฅผ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค. repositoryImpl.setDataSource(dataSource); // persistent_logins ํ…Œ์ด๋ธ” ์ƒ์„ฑ try { repositoryImpl.getJdbcTemplate().execute(JdbcTokenRepositoryImpl.CREATE_TABLE_SQL); } catch (BadSqlGrammarException e) { log.error("persistent_logins ํ…Œ์ด๋ธ”์ด ์ด๋ฏธ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค."); } catch (Exception e) { log.error("์ž๋™ ๋กœ๊ทธ์ธ ํ…Œ์ด๋ธ” ์ƒ์„ฑ ์ค‘ , ์˜ˆ์™ธ ๋ฐœ์ƒ"); } return repositoryImpl; }
Java
๋ณต์‚ฌ

~/config/SecurityConfig.java

@Slf4j @Configuration @EnableWebSecurity public class SecurityConfig { @Autowired private DataSource dataSource; // ์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ ์„ค์ • ๋ฉ”์†Œ๋“œ @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { // โœ… ์ธ๊ฐ€ ์„ค์ • http.authorizeRequests(requests -> requests .antMatchers("/**").permitAll() .anyRequest().permitAll() ); // ๐Ÿ” ํผ ๋กœ๊ทธ์ธ ์„ค์ • http.formLogin(withDefaults()); // ๐Ÿ”„ ์ž๋™ ๋กœ๊ทธ์ธ ์„ค์ • http.rememberMe(me -> me.key("aloha") .tokenRepository(tokenRepository()) .tokenValiditySeconds(60 * 60 * 24 * 7)); return http.build(); } /** * ๐Ÿ‘ฎโ€โ™‚๏ธ๐Ÿ” ์‚ฌ์šฉ์ž ์ธ์ฆ ๊ด€๋ฆฌ ๋นˆ ๋“ฑ๋ก ๋ฉ”์†Œ๋“œ * JDBC ์ธ์ฆ ๋ฐฉ์‹ * โœ… ๋ฐ์ดํ„ฐ ์†Œ์Šค (URL, ID, PW) - application.properties * โœ… SQL ์ฟผ๋ฆฌ ๋“ฑ๋ก * โญ ์‚ฌ์šฉ์ž ์ธ์ฆ ์ฟผ๋ฆฌ * โญ ์‚ฌ์šฉ์ž ๊ถŒํ•œ ์ฟผ๋ฆฌ * @return */ @Bean public UserDetailsService userDetailsService() { JdbcUserDetailsManager userDetailsManager = new JdbcUserDetailsManager(dataSource); // ์‚ฌ์šฉ์ž ์ธ์ฆ ์ฟผ๋ฆฌ String sql1 = " SELECT username, password, enabled " + " FROM user " + " WHERE username = ? " ; // ์‚ฌ์šฉ์ž ๊ถŒํ•œ ์ฟผ๋ฆฌ String sql2 = " SELECT username, auth " + " FROM user_auth " + " WHERE username = ? " ; userDetailsManager.setUsersByUsernameQuery(sql1); userDetailsManager.setAuthoritiesByUsernameQuery(sql2); return userDetailsManager; } /** * ๐Ÿƒ ์ž๋™ ๋กœ๊ทธ์ธ ์ €์žฅ์†Œ ๋นˆ ๋“ฑ๋ก * โœ… ๋ฐ์ดํ„ฐ ์†Œ์Šค * โญ persistent_logins ํ…Œ์ด๋ธ” ์ƒ์„ฑ create table persistent_logins ( username varchar(64) not null , series varchar(64) primary key , token varchar(64) not null , last_used timestamp not null ); * ๐Ÿ”„ ์ž๋™ ๋กœ๊ทธ์ธ ํ”„๋กœ์„ธ์Šค * โœ… ๋กœ๊ทธ์ธ ์‹œ * โžก ๐Ÿ‘ฉโ€๐Ÿ’ผ(ID, ์‹œ๋ฆฌ์ฆˆ, ํ† ํฐ) ์ €์žฅ * โœ… ๋กœ๊ทธ์•„์›ƒ ์‹œ, * โžก ๐Ÿ‘ฉโ€๐Ÿ’ผ(ID, ์‹œ๋ฆฌ์ฆˆ, ํ† ํฐ) ์‚ญ์ œ * @return */ @Bean public PersistentTokenRepository tokenRepository() { // JdbcTokenRepositoryImpl : ํ† ํฐ ์ €์žฅ ๋ฐ์ดํ„ฐ ๋ฒ ์ด์Šค๋ฅผ ๋“ฑ๋กํ•˜๋Š” ๊ฐ์ฒด JdbcTokenRepositoryImpl repositoryImpl = new JdbcTokenRepositoryImpl(); // โœ… ํ† ํฐ ์ €์žฅ์†Œ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐ์ดํ„ฐ ์†Œ์Šค ์ง€์ • // - ์‹œํ๋ฆฌํ‹ฐ๊ฐ€ ์ž๋™ ๋กœ๊ทธ์ธ ํ”„๋กœ์„ธ์Šค๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ DB๋ฅผ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค. repositoryImpl.setDataSource(dataSource); // persistent_logins ํ…Œ์ด๋ธ” ์ƒ์„ฑ try { repositoryImpl.getJdbcTemplate().execute(JdbcTokenRepositoryImpl.CREATE_TABLE_SQL); } catch (BadSqlGrammarException e) { log.error("persistent_logins ํ…Œ์ด๋ธ”์ด ์ด๋ฏธ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค."); } catch (Exception e) { log.error("์ž๋™ ๋กœ๊ทธ์ธ ํ…Œ์ด๋ธ” ์ƒ์„ฑ ์ค‘ , ์˜ˆ์™ธ ๋ฐœ์ƒ"); } return repositoryImpl; } }
Java
๋ณต์‚ฌ