1. 개요
[Spring Boot] security를 사용한 user 생성 및 권한부여 -1 에서는 메모리에 user 정보를 올리고 사용하는 위험한 행동을 했었다..
그래서 mariaDB를 설치하고 user를 생성해서 비밀번호를 암호화하고, 권한까지 부여해서 테스트를 진행해보기로 했다.
2. 구현
* /src/main/resource/template 하위에 모든 html 페이지를 두고 application.yml에 아래와 같이 추가 후 진행.
############################추가############################
spring:
thymeleaf:
prefix: classpath:/template
###########################################################
2-1) User class 작성
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name="USER")
public class User {
@Id
private String username;//PK
private String passwd;
private String role;
//Getter Setter 생략
}
* 주의 *
@Table(name="USER")
mariaDB에 Table 명을 대문자로 USER로 작성해서 위와 같이 입력하였는데, 그대로 실행하면 'user' Table을 찾을 수 없다면서 에러가 발생한다.
대문자로 인식하도록 application.yml에 아래와 같이 추가해주어야 한다.
############################추가############################
spring:
jpa:
hibernate:
naming:
physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
###########################################################
2-2) UserService 작성
public interface UserService {
void saveUser(String id,String passwd);
}
2-3) UserServiceImpl 작성
@Service
public class UserServiceImpl implements UserService{
@Autowired
private UserRepository userRepository;
@Bean
PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
@Override
public void saveUser(String username, String passwd) {
User user = new User();
user.setUsername(username);
user.setPasswd(passwordEncoder().encode(passwd));
user.setRole("USER");
userRepository.save(user);
}
user.setRole("USER"); <- 생성되는 모든 신규 user에 대해서는 USER role만 부여하겠다는 말이다.
BCryptPasswordEncoder()를 사용해서 password를 암호화해서 저장한다.
2-4) UserRepository 작성
public interface UserRepository extends CrudRepository<User, Long>{
}
2-5) Security 작성
@Component
@EnableWebSecurity
public class Security extends WebSecurityConfigurerAdapter {
@Autowired
private DataSource dataSource;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/admin").hasAuthority("ADMIN")
.antMatchers("/**").permitAll() // 넓은 범위의 URL을 아래에 배치한다.
.anyRequest().authenticated()
.and()
.formLogin().loginPage("/login").failureUrl("/login?error").permitAll()
.and()
.logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.addLogoutHandler(new TaskImplementingLogoutHandler()).permitAll().logoutSuccessUrl("/");
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication()
.dataSource(dataSource)
.rolePrefix("ROLE_")
//앞에 ROLE_을 붙여야 home.html에서 정상적으로 hasRole()구문을 인식
.usersByUsernameQuery("select username, replace(passwd, '$2y', '$2a'), true from USER where username = ?")
.authoritiesByUsernameQuery("select username, role from USER where username = ?");
}
@Bean
PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}
* https://www.browserling.com/tools/bcrypt -> passwd BCrypt로 변환 사이트이다. 테스트를 위해 admin계정과 manager 계정은 직접 변환해서 DB에 입력해 주었다.
* .usersByUsernameQuery("select username, replace(passwd, '$2y', '$2a'), true from USER where username = ?")
위 부분은 BCrypt로 변환했을 경우 prefix가 $2y로 되는 경우가 있다고 하여 일치하도록 치환을 진행한 후에 query를 수행하는 부분이다.
2-6) Controller 작성
@Controller
public class TestController {
@Autowired
UserService userService;
@GetMapping("/")
public String home(ModelAndView mav) {
return "/home.html";
}
@ResponseBody
@GetMapping("/test")
public String test() {
return "OK";
}
@ResponseBody
@GetMapping("/adminOnly")
public String adminOnly() {
return "Secret Page";
}
@GetMapping("/login")
public String loginForm() {
return "/login-form.html";
}
@GetMapping("/sginup")
public String sginupForm() {
return "/sginup-form.html";
}
@PostMapping("/sginup")
public String sginupProcess(@Param("username") String username, @Param("passwd") String passwd) {
return "/home.html";
}
}
2-7) Custom page 작성 - login-form.html(http://yoonbumtae.com/?p=1184 참고), sginup-form.html
login-form.html
<form name="f" th:action="@{/login}" method="post">
<fieldset>
<legend>Please Login</legend>
<div th:if="${param.error}" class="alert alert-error">
Invalid username and password.
</div>
<div th:if="${param.logout}" class="alert alert-success">
You have been logged out.
</div>
<label for="username">Username</label>
<input type="text" id="username" name="username"/>
<label for="password">Password</label>
<input type="password" id="password" name="password"/>
<div class="form-actions">
<button type="submit" class="btn">Log in</button>
</div>
</fieldset>
</form>
sginup-form.html
<form name="UserRegistrationForm" th:action="@{/sginup}" method="post">
<input type="text" name="username" placeholder="아이디를 입력해주세요">
<input type="password" name="passwd" placeholder="비밀번호">
<button type="submit">가입하기</button>
</form>
3. 테스트
3-1) 초기 화면

3-2) 회원 가입
-. DB에 실제 생성 된 user 확인. 새로 생성된 user_test는 password가 암호화가 되어 들어갔지만 직접 생성한 user 계정은 암호화가 되어있지 않다.
실제로 user 계정을 사용해서 접속하면 login이 되지 않는다.

3-3) Login
-. user_test 계정으로 접속



