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 계정으로 접속