[Spring] Setting up a New User for Todo Application
Java Web Application with Spring and Hibernate (10)
/ UserDetails / createNewUser / HttpSecurity / withDefaults() / @Entity / SecurityFilterChain / authorizeHttpRequests / JpaRepository
Setting up a New User
before SpringSecurityConfiguration
@Configuration
public class SpringSecurityConfiguration {
@Bean
public InMemoryUserDetailsManager createUserDetailsManager() {
Function<String, String> passwordEncoder
= input -> passwordEncoder().encode(input);
UserDetails userDetails = User.builder()
.passwordEncoder(passwordEncoder)
.username("in28minutes")
.password("dummy")
.roles("USER","ADMIN")
.build();
return new InMemoryUserDetailsManager(userDetails);
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
after SpringSecurityConfiguration
@Configuration
public class SpringSecurityConfiguration {
@Bean
public InMemoryUserDetailsManager createUserDetailsManager() {
UserDetails userDetails1 = createNewUser("in28minutes", "dummy");
UserDetails userDetails2 = createNewUser("ranga", "dummydummy");
return new InMemoryUserDetailsManager(userDetails1, userDetails2);
}
private UserDetails createNewUser(String username, String password) {
Function<String, String> passwordEncoder
= input -> passwordEncoder().encode(input);
UserDetails userDetails = User.builder()
.passwordEncoder(passwordEncoder)
.username(username)
.password(password)
.roles("USER","ADMIN")
.build();
return userDetails;
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
Spring Boot starter Data JPA and H2
Dependency 추가
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
h2
resources/application.properties
- h2 연결
- 콘솔에 있는 url 복사
spring.datasource.url=jdbc:h2:mem:testdb
SpringSecurityConfiguration
- add SecurityFilterChain
//All URLs are protected //A login form is shown for unauthorized requests //CSRF disable //Frames @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.authorizeHttpRequests( auth -> auth.anyRequest().authenticated()); http.formLogin(withDefaults()); http.csrf().disable(); http.headers().frameOptions().disable(); return http.build(); } }
- 스프링 시큐리티를 적용하면 CSRF 기능이 동작한다.
- 이 기능 때문에 h2 콘솔창이 비활성화 된다. (h2콘솔이 스프링과 상관 없는 일반 어플리케이션이라 그렇다고 한다. + 모든 url이 보호되기 때문에 어떤 url 이든 로그인이 안되어 있으면 로그인 창 부터 나옴)
- SecurityFilterChain
- 웹 요청이 들어올 때마다 HTTP 서블릿 요청과 일치시킬 수 있음
- HttpSecurity 에서 author을 정의해줘야함 -> authorizeHttpRequests
- withDefaults() 는 Customizer에서 static 메서드 사용
- http.formLogin(withDefaults()); 를 통해서 로그인 되지 않는 폼을 체크 (아이디, 비번등)
- 참고 : https://wikidocs.net/162150
- 참고 : https://velog.io/@kose/SecurityFilterChain-%EB%8B%A4%EC%A4%91-%ED%95%84%ED%84%B0-%EA%B4%80%EB%A6%AC
- CSRF 를 사용하지 않으니 http.headers().frameOptions() 를 통해 frame도 disable로
JPA Bean 데이터 베이스에 연결
- JPA를 사용하면 빈을 데이터베이스에 매핑할 수 있다.
- @Entity 를 추가하면 된다.
- Todo가 Entity라고 하면 이 빈이 데이터베이스 테이블에 매핑이 된다는 뜻이다.
before Todo.java
//Database (MySQL)
//Static List of todos => Database (H2, MySQL)
public class Todo {
public Todo(int id, String username, String description, LocalDate targetDate, boolean done) {
super();
this.id = id;
this.username = username;
this.description = description;
this.targetDate = targetDate;
this.done = done;
}
private int id;
private String username;
@Size(min=10, message="Enter at least 10 characters")
private String description;
private LocalDate targetDate;
private boolean done;
....
}
//Database (MySQL)
//Static List of todos => Database (H2, MySQL)
//JPA
// Bean -> Database Table
@Entity
public class Todo {
public Todo(int id, String username, String description, LocalDate targetDate, boolean done) {
super();
this.id = id;
this.username = username;
this.description = description;
this.targetDate = targetDate;
this.done = done;
}
@Id
@GeneratedValue
private int id;
private String username;
@Size(min=10, message="Enter atleast 10 characters")
private String description;
private LocalDate targetDate;
private boolean done;
....
}
- Primary key로 @Id를 선언해서 사용하면 된다.
- 시퀀스를 사용해서 Id 값을 생성하기 위해서 @GeneratedValue를 사용해주면 된다.
- 뭔말이냐면 id 값을 자동으로 생성해주는 에노테이션이다.
- @Column이나 @Entitiy도 (name= ) 을통해 이름을 컨트롤 할 수 있다.
- 스프링에서 @Entitiy가 탐색 되면 바로 Todo 클래스 빈을 만든다.
before h2
after h2
sql문
/src/main/resources/data.sql
insert into todo (ID, USERNAME, DESCRIPTION, TARGET_DATE, DONE)
values(10001,'in28minutes', 'Get AWS Certified', CURRENT_DATE(), false);
insert into todo (ID, USERNAME, DESCRIPTION, TARGET_DATE, DONE)
values(10002,'in28minutes', 'Get Azure Certified', CURRENT_DATE(), false);
insert into todo (ID, USERNAME, DESCRIPTION, TARGET_DATE, DONE)
values(10003,'in28minutes', 'Get GCP Certified', CURRENT_DATE(), false);
insert into todo (ID, USERNAME, DESCRIPTION, TARGET_DATE, DONE)
values(10004,'in28minutes', 'Learn DevOps', CURRENT_DATE(), false);
- sql에서는 ‘’ 작은 따옴표를 사용한다.
/src/main/resources/application.properties
spring.jpa.defer-datasource-initialization=true
- @Entitiy 가 sql 파일보다 먼저 실행된다.
- 따라서 테이블이 생성되면 그 후에 @Entity가 실행 될 수 있게 환경 설정을 위와 같이 해줘야 한다.
- 그럼 다음과 같이 잘 작동된다.
Creating TodoRepository & Connecting List Todos page from H2 database
- Repository allows you to perorm actions on entities
/src/main/java/com/in28minutes/springboot/myfirstwebapp/todo/TodoRepository.java
```java
public interface TodoRepository extends JpaRepository<Todo, Integer>{
public List
>- JpaRepository< 1 , 2 >
> - 1은 어떤 빈을 관리할 건지
> - 2는 아이디 필드의 타입을 뭘로 할 건지
>- Spring data JPA가 자동으로 username을 찾는 메소드를 생성해준다.
### before Todo.java
```java
@Entity
public class Todo {
public Todo(int id, String username, String description, LocalDate targetDate, boolean done) {
super();
this.id = id;
this.username = username;
this.description = description;
this.targetDate = targetDate;
this.done = done;
}
after Todo.java
@Entity
public class Todo {
public Todo(){ <----
}
public Todo(int id, String username, String description, LocalDate targetDate, boolean done) {
super();
this.id = id;
this.username = username;
this.description = description;
this.targetDate = targetDate;
this.done = done;
}
- Entity 에 대한 디폴트 생성자를 추가해줌
before TodoControllerJpa.java
@Controller
@SessionAttributes("name")
public class TodoControllerJpa {
public TodoControllerJpa(TodoService todoService) {
super();
this.todoService = todoService;
}
private TodoService todoService;
@RequestMapping("list-todos")
public String listAllTodos(ModelMap model) {
String username = getLoggedInUsername(model);
List<Todo> todos = todoService.findByUsername(username);
model.addAttribute("todos", todos);
return "listTodos";
}
after TodoControllerJpa.java
@Controller
@SessionAttributes("name")
public class TodoControllerJpa {
public TodoControllerJpa(TodoService todoService, TodoRepository todoRepository) {
super();
this.todoService = todoService; <----
this.todoRepository = todoRepository;
}
private TodoService todoService; <----
private TodoRepository todoRepository;
@RequestMapping("list-todos")
public String listAllTodos(ModelMap model) {
String username = getLoggedInUsername(model);
List<Todo> todos = todoRepository.findByUsername(username); <---
model.addAttribute("todos", todos);
return "listTodos";
}
- 이렇게 하면 static에 있는 정보가 아닌 h2 database로 부터 데이터를 갖고 온다.
댓글남기기