Spring Security -JDBC Authentication
์คํ๋ง ์ํ๋ฆฌํฐ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ์ฐ๋ํ์ฌ ์ธ๊ฐ, ์ธ์ฆ ๋ณด์์ ํ๋ ค๋ฉด 3๊ฐ์ง๊ฐ ์์ด์ผ ํ๋ค.
-config ํด๋์ค์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๊ด๋ จ๋ ์ ๋ณด๊ฐ ์์ด์ผํ๋ค. (Datasource)
-users์ authorities ํ ์ด๋ธ์ด ์์ด์ผ ํ๋ค.
-๊ธฐ์กด์ ํ๋ In Memory Authentication ์ค์ ์ ๊ฑฐํด์ค์ผํ๋ค.
users์ authorities ํ ์ด๋ธ ์์ฑ
CREATE TABLE users (
username VARCHAR2(50) NOT NULL,
password VARCHAR2(120) NOT NULL,
enabled CHAR(1) NOT NULL,
CONSTRAINT check_users_enabled CHECK(enabled IN('y','n')),
CONSTRAINT pk_users_username PRIMARY KEY(username)
);
CREATE TABLE authorities (
username VARCHAR2(50) NOT NULL,
authority VARCHAR2(50) NOT NULL,
CONSTRAINT fk_authorities_username FOREIGN KEY(username) REFERENCES users(username)
);
INSERT INTO users VALUES('smith', 'smith','y');
INSERT INTO authorities VALUES('smith', 'ROLE_ADMIN');
INSERT INTO users VALUES('blake', 'blake','y');
INSERT INTO authorities VALUES('blake', 'ROLE_USER');
INSERT INTO users VALUES('jones', 'jones','y');
INSERT INTO authorities VALUES('jones', 'ROLE_GUEST');
--'smith' ๋ฌธ์์ด ์ํธํ
UPDATE users SET password='$2a$10$LWbsj6DG/JmWkz3uY9I89.xf2/9UpaWaMgLLKb1PMIkpxZwTACOqS' WHERE username='smith';
--'blake' ๋ฌธ์์ด ์ํธํ
UPDATE users SET password='$2a$10$B3wLDsAfD3vpGv3tl8X/xe.8c7SCrHubEOqts0PA5e49cB5al3RP2' WHERE username='blake';
--'jones' ๋ฌธ์์ด ์ํธํ
UPDATE users SET password='$2a$10$KIjMtKbXBRtL5kolqFGEIehF40IZa5kCkcrbZfNWHtRCmNF3/V7WO' WHERE username='jones';
๊ฒฐ๊ณผ :
config ํด๋์ค์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๊ด๋ จ๋ ์ ๋ณด๊ฐ ์์ด์ผํ๋ค. (Datasource) & ๊ธฐ์กด์ ํ๋ In Memory Authentication ์ค์ ์ ๊ฑฐํด์ค์ผํ๋ค.
SimpleSecurityConfig1.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
|
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
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.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Configuration
@EnableWebSecurity
public class SimpleSecurityConfig1
{
@Autowired
DataSource dataSource; // JDBC Authentication์ ํ์ํจ
@Bean
public BCryptPasswordEncoder passwordEncoder() {
BCryptPasswordEncoder enc = new BCryptPasswordEncoder();
log.info("smith->" + enc.encode("smith")); // ROLE_ADMIN
log.info("blake->" + enc.encode("blake")); // ROLE_USER
log.info("jones->" + enc.encode("jones")); // ROLE_GUEST
return enc;
}
/*
* smith->$2a$10$kqk98pSUNsPwdnzED3IC5./1Qw/MukfF33WhdQjRhvhzygmiISriu
* blake->$2a$10$peWBE7KRBkxj3h9WHjiTQuplVc.homrK.MumRdV3gfc6hC4qShcYG
* jones->$2a$10$x1bf..r/ZOewaq9xPLw2nuYRMFZwBbgHBAiEOvuNhQEYuIPzHz/SC
*/
//Enable jdbc authentication
@Autowired
public void configAuthentication(AuthenticationManagerBuilder auth) throws Exception {
log.info("๋ฐ์ดํฐ์์ค ์ค์ ");
auth.jdbcAuthentication().dataSource(dataSource);
}
@Bean
public WebSecurityCustomizer webSecurityCustomizer() {
return (web) -> web.ignoring().antMatchers("/resources/**", "/ignore2");
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
log.info("์ ๊ทผ์ ํ ์ค์ ");
return http.authorizeHttpRequests()/* ๊ถํ์ ๋ฐ๋ฅธ ์ธ๊ฐ(Authorization) */
.antMatchers("/", "/sec/", "/sec/loginForm", "/sec/denied", "/logout").permitAll()
.antMatchers("/sec/hello").hasAnyRole("USER", "ADMIN")
.antMatchers("/sec/getemps").hasAnyRole("USER", "ADMIN")
.antMatchers("/sec/addemp").hasAnyRole("ADMIN")
.antMatchers("/sec/main").hasAnyRole("USER","GUEST","ADMIN")
.antMatchers("/sec/sample").hasAnyRole("GUEST", "ADMIN")
//.anyRequest().authenticated() // ์์ ์ค์ ์ด์ธ์ ๋ชจ๋ ์์ฒญ์ ์ธ์ฆ์ ๊ฑฐ์ณ์ผ ํ๋ค
.anyRequest().permitAll() // ์์ ์ค์ ์ด์ธ์ ๋ชจ๋ ์์ฒญ์ ์ธ์ฆ ์๊ตฌํ์ง ์์
.and()
.csrf().disable() //csrf ๊ธฐ๋ฅ์ ์ฌ์ฉํ์ง ์์ ๋
//.csrf().ignoringAntMatchers("/logout") //์์ฒญ์ 'POST' not supported ์๋ฌ ๋ฐฉ์ง
//.ignoringAntMatchers("/sec/loginForm")
//.ignoringAntMatchers("/doLogin")
.formLogin().loginPage("/sec/loginForm") // ์ง์ ๋ ์์น์ ๋ก๊ทธ์ธ ํผ์ด ์ค๋น๋์ด์ผ ํจ
.loginProcessingUrl("/doLogin") // ์ปจํธ๋กค๋ฌ ๋ฉ์๋ ๋ถํ์, ํผ action๊ณผ ์ผ์นํด์ผ ํจ
.failureUrl("/sec/login-error") // ๋ก๊ทธ์ธ ์คํจ์ ๋ค์ ๋ก๊ทธ์ธ ํผ์ผ๋ก
//.failureForwardUrl("/login?error=Y") //์คํจ์ ๋ค๋ฅธ ๊ณณ์ผ๋ก forward
.defaultSuccessUrl("/sec/main", true)
.usernameParameter("userid") // ๋ก๊ทธ์ธ ํผ์์ ์ด์ฉ์ ID ํ๋ ์ด๋ฆ, ๋ํดํธ๋ username
.passwordParameter("userpwd") // ๋ก๊ทธ์ธ ํผ์์ ์ด์ฉ์ ์ํธ ํํธ ์ด๋ฆ, ๋ํดํธ๋ password
.permitAll()
.and() // ๋ํดํธ ๋ก๊ทธ์์ URL = /logout
.logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout")) //๋ก๊ทธ์์ ์์ฒญ์ URL
.logoutSuccessUrl("/sec/loginForm?logout=T")
.invalidateHttpSession(true)
.deleteCookies("JSESSIONID")
.permitAll()
.and()
.exceptionHandling().accessDeniedPage("/sec/denied")
.and().build();
}
/* ์๋์ ๋ด์ฉ์ In-Memory Authentication์ ์ฌ์ฉ๋๋ฏ๋ก JDBC Authentication์๋ ๋ถํ์
@Autowired
public void configureGlobal(AuthenticationManagerBuilder authenticationMgr) throws Exception {
authenticationMgr.inMemoryAuthentication().withUser("employee").password("$2a$10$MZ2ANCUXIj5mrAVbytojruvzrPv9B3v9CXh8qI9qP13kU8E.mq7yO")
.authorities("ROLE_USER").and().withUser("imadmin").password("$2a$10$FA8kEOhdRwE7OOxnsJXx0uYQGKaS8nsHzOXuqYCFggtwOSGRCwbcK")
.authorities("ROLE_USER", "ROLE_ADMIN").and().withUser("guest").password("$2a$10$ABxeHaOiDbdnLaWLPZuAVuPzU3rpZ30fl3IKfNXybkOG2uZM4fCPq")
.authorities("ROLE_GUEST");
}*/
}
|
cs |
config ํด๋์ค์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๊ด๋ จ๋ ์ ๋ณด๊ฐ ์์ด์ผํ๋ค. (Datasource) > 23 ํ ~ 45 ํ
๊ธฐ์กด์ ํ๋ In Memory Authentication ์ค์ ์ ๊ฑฐํด์ค์ผํ๋ค. > 92ํ ~ 99ํ
SecurityController.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping("/sec")
public class SecurityController {
@GetMapping("/")
public String index() {
return "thymeleaf/index";
}
@GetMapping("/loginForm")
public String loginForm() {
return "thymeleaf/loginForm";
}
@GetMapping("/main")
public String goMain() {
return "thymeleaf/main";
}
@GetMapping("/hello")
@ResponseBody
public String goHello() {
return "๋น์ ์ ์ด์ฉ์ ๋๋ ๊ด๋ฆฌ์์
๋๋ค.";
}
@GetMapping("/login-error")
public String fail(Model model) {
model.addAttribute("loginError",true);
return "thymeleaf/loginForm";
}
}
|
cs |
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/extras/thymeleaf-extras-springsecurity5">
<head>
<meta charset="utf-8">
<title>INDEX</title>
</head>
<body>
<h2>Welcome</h2>
<p>Spring Security Thymeleaf tutorial</p>
<div sec:authorize="isAnonymous()">
You should login in for using this.
</div>
<div sec:authorize="hasRole('USER')">
Text visible to user.
</div>
<div sec:authorize="hasRole('ADMIN')">
Text visible to admin.
</div>
<div sec:authorize="hasAnyRole('ADMIN','USER')">
Text visible to admin or user.
</div>
<div sec:authorize="isAuthenticated()">
<div>Authenticated username:
<span sec:authentication="name"></span>
</div>
<div>Authenticated user roles:
<span sec:authentication="principal.authorities"></span>
</div>
</div>
</body>
</html>
|
cs |
loginForm.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>๋ก๊ทธ์ธํด์ฃผ์ธ์</title>
<style>
h1{width:fit-content; margin:0 auto;}
label{display:inline-block;width:3em; text-align: right;
margin-right:1em; }
input { width: 5em; }
form {width:fit-content; padding:0.5em; margin:0 auto;
border:1px solid black;}
button { margin: 5px; }
div:last-child { text-align: center;}
.error {color: red; text-align: center;}
</style>
</head>
<body>
<h1>๋ก๊ทธ์ธ</h1>
<p th:if= "${loginError}" class="error">Wrong userid or password</p>
<form action="/doLogin" method="post">
<div><label>์์ด๋</label>
<input id="uid" type="text" name="userid" value="">
</div>
<div><label>์ ํธ</label>
<input id="pwd" type="password" name="userpwd" value="">
</div>
<div><button type="submit">๋ก๊ทธ์ธ</button></div>
</form>
</body>
</html>
|
cs |
main.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
<head>
<meta charset="utf-8">
<title>Spring Boot Thymeleaf Test</title>
<!-- <link href="/css/common.css" rel="stylesheet" /> -->
</head>
<body>
<!-- <th:block th:include="@{/thymeleaf/fragments/header}"></th:block> -->
<div class="content">
<h2>This is Content</h2>
<div th:each="item ,status: ${list}" th:text="${item}"
th:id="|id_${status.index}|"></div>
</div>
<a th:href="${'/logout'}">๋ก๊ทธ์์</a>
<!-- <th:block th:include="@{/thymeleaf/fragments/footer}"></th:block> -->
</body>
</html>
|
cs |
์คํ๊ฒฐ๊ณผ :
๋๊ธ