이번에 설계한 것은 로그인 페이지를 통해서
아이디와 비번을 입력했을때 사용자를 인증하는 프로젝트 입니다.
applicaton.properties 설계
spring.datasource.driver-class-name = com.mysql.cj.jdbc.Driver
spring.datasource.url = jdbc:mysql://localhost/jpa_ex?characterEncoding=utf8&serverTimezone=Asia/Seoul
spring.datasource.username = root
spring.datasource.password = root
spring.jpa.hibernate.ddl-auto=create
spring.jpa.generate-ddl=false
spring.jpa.show-sql=true
spring.jpa.database=mysql
logging.level.org.hibernate=info
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
spring.thymeleaf.cache=false
html 설계
(src/main/resources/templates)
1) index.html
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Thymeleaf3</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
</head>
<body>
<h1>Index Page for Everyine!!</h1>
</body>
</html>
2) guest.html
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Thymeleaf3</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
</head>
<body>
<h1>Guest Page for Everyine!!</h1>
</body>
</html>
3) admin.html
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Thymeleaf3</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
</head>
<body>
<h1>Administer Page for Everyine!!</h1>
</body>
</html>
4) manager.html
<!DOCTYPE html>
<html>
<head>
<meta charset="EUC-KR">
<title>Insert title here</title>
</head>
<body>
<h1>Login Success ^^ !!!</h1>
</body>
</html>
5) success.html
<!DOCTYPE html>
<html>
<head>
<meta charset="EUC-KR">
<title>Insert title here</title>
</head>
<body>
<h1>Login Success ^^ !!!</h1>
</body>
</html>
6) login.html
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Thymeleaf3</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<h2>CUSTOM LOGIN PAGE</h2>
<div th:if="${param.error != null}">
<h2>Invalid Username or password</h2>
<h2 th:value="${param.error}"></h2>
</div>
<div th:if="${param.logout !=null}">
<h2>You have been logged out.</h2>
</div>
<form method="post">
<p>
<label for="username">Username</label> <input type="text"
id="username" name="username"/>
</p>
<p>
<label for="password">Password</label> <input type="password"
id="password" name="password"/>
</p>
<input type="hidden" th:name="${_csrf.parameterName}"
th:value="${_csrf.token}"/>
<button type="submit" class="btn">Log in</button>
</form>
</body>
</html>
7) logout.html
<!DOCTYPE html>
<html>
<head>
<meta charset="EUC-KR">
<title>Insert title here</title>
</head>
<body>
<h2>CUSTOM LOGOUT PAGE</h2>
<form method="post">
<h3>Logout</h3>
<input type="hidden" th:name="${_csrf.parameterName}"
th:value="${_csrf.token}" />
<button type="submit" class="btn">LogOut</button>
</form>
</body>
</html>
8) accessDenied.html
<!DOCTYPE html>
<html>
<head>
<meta charset="EUC-KR">
<title>AccessDenied</title>
</head>
<body>
<div class="panel panel-default">
<div class="panel-body">
<div class="alert alert-danger" role="alert">
<h2>Sorry, you do not have permission to view this page.</h2>
Click Login at <a th:href="@{/login}">here</a>
</div>
</div>
</div>
</body>
</html>
9) adminSecret.html
<!DOCTYPE html>
<html>
<head>
<meta charset="EUC-KR">
<title>Guest Test</title>
</head>
<body>
<h1>Administer Secret</h1>
</body>
</html>
10) managerSecret.html
<!DOCTYPE html>
<html>
<head>
<meta charset="EUC-KR">
<title>Guest Test</title>
</head>
<body>
<h1>Manager Secret</h1>
</body>
</html>
SecurityConfig 설계
(org.zerock.security.SecurityConfig)
- 로그인 할때 입력해야 하는 아이디, 암호 그리고 입력시 어느 html이 작동되는지를 설계합니다.
package org.zerock.security;
import org.springframework.context.annotation.Bean;
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.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.PasswordEncoder;
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter{
@Override
protected void configure(HttpSecurity http) throws Exception {
//아이디, 암호가 입력시 조건에 따라 이동될 html 파일들이 결정된다.
http.authorizeRequests().antMatchers("/guest/**").permitAll()
.and()
.authorizeRequests().antMatchers("/manager/**").hasRole("MANAGER")
.and()
.authorizeRequests().antMatchers("/admin/**").hasRole("ADMIN")
.and()
.formLogin().loginPage("/login").defaultSuccessUrl("/success").permitAll()
.and()
.exceptionHandling().accessDeniedPage("/accessDenied")
.and()
.logout().logoutUrl("/logout").invalidateHttpSession(true);
}
@Bean
public PasswordEncoder passwordEncoder() { //입력한 암호가 맞는지 검사를 해줍니다.
return new PasswordEncoder() {
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
// TODO Auto-generated method stub
return rawPassword.equals(encodedPassword);
}
@Override
public String encode(CharSequence rawPassword) {
// TODO Auto-generated method stub
return rawPassword.toString();
}
};
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// 페이지에 입력될 아이디와 암호들
auth.inMemoryAuthentication()
.withUser("manager").password("1111").roles("MANAGER")
.and()
.withUser("guest").password("2222").roles("BASIC")
.and()
.withUser("admin").password("3333").roles("ADMIN");
}
}
MemberRepository 설계
(org.zerock.persistence.MemberRepository.java)
package org.zerock.persistence;
import org.springframework.data.jpa.repository.JpaRepository;
import org.zerock.domain.Member;
public interface MemberRepository extends JpaRepository<Member, String>{
}
SampleController 설계
(org.zerock.controller.SampleController.java)
페이지를 생성하고 나중에 보안을 적용해서 접근을 제어한다.
package org.zerock.controller;
import java.util.Arrays;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.zerock.domain.Member;
import org.zerock.domain.MemberRole;
import org.zerock.persistence.MemberRepository;
@Controller
public class SampleController {
@Autowired
private MemberRepository repo;
@GetMapping("/")
public String index() {
return "index";
}
@RequestMapping("/guest")
public String forGuest() {
return "guest";
}
@RequestMapping("/manager")
public String forManager() {
return "manager";
}
@RequestMapping("/admin")
public String forAdmin() {
return "admin";
}
@PostConstruct
public void init() { //101명의 사용자를 생성
for (int i = 0; i <= 100; i++) {
Member member = new Member();
member.setUid(i);
member.setUpw("pw"+i);
member.setUname("사용자"+i);
MemberRole role = new MemberRole();
if (i<=80) { //user0~user80까지는 BASIC 권한 가짐
role.setRoleName("BASIC");
} else if(i<=90) { //user90까지는 MANAGER 권한 가짐
role.setRoleName("MANAGER");
} else { //나머지 10명은 ADMIN 권한 가짐
role.setRoleName("ADMIN");
}
member.setRoles(Arrays.asList(role));
repo.save(member);
}
}
}
Member 클래스 설계
(org.zerock.domain.Member.java)
package org.zerock.domain;
import java.time.LocalDateTime;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@Entity
@Table(name="tbl_members")
@EqualsAndHashCode(of="uid")
@ToString
public class Member {
@Id
private long uid; //회원 아이디
private String upw; //user password
private String uname; //user name
@CreationTimestamp
private LocalDateTime regdate;
@UpdateTimestamp
private LocalDateTime updatedate;
@OneToMany(cascade = CascadeType.ALL, fetch=FetchType.EAGER)
@JoinColumn(name="member")
private List<MemberRole> roles; //MemberRole과 연관관계 설정
}
회원의 아이디를 의미하는 uid와 upw, uname 등의 속성을 가지도록 설계한다.
MemberRole 설계
(org.zerock.domain.MemberRole.java)
package org.zerock.domain;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@Entity
@Table(name = "tbl_member_roles")
@EqualsAndHashCode(of="fno")
@ToString
public class MemberRole {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long fno;
private String roleName;
}
회원이 가지는 권한에 대한 이름을 가지는 구조
LoginController 설계
(org.zerock.controller.LoginController.java)
package org.zerock.controller;
import org.springframework.security.access.annotation.Secured;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class LoginController {
@Secured({"ADMIN", "MANAGER"})
@GetMapping("/login")
public String login() {
return "login";
}
@GetMapping("/accessDenied")
public String accessDenied() {
return "accessDenied";
}
@GetMapping("/logout")
public String logout() {
return "logout";
}
@GetMapping("/success")
public String success() {
return "success";
}
@Secured({"ADMIN", "MANAGER"})
@RequestMapping("/managerSecret")
public String managerSecret() {
return "managerSecret";
}
@Secured({"ADMIN"})
@RequestMapping("/adminSecret")
public String adminSecret() {
return "adminSecret";
}
}
결과
SecurityConfig에 입력해 놓은 아이디와 비밀번호를 입력하면 위처럼 로그인에 성공한다.
그러나 다른 아이디나 암호를 치면 위처럼 Invalid Username or password가 뜨면서 로그인에 실패한다.
위 두 사진은 LoginController에서 Secured에 따라 표시된다.
managerSecret은 관리자와 매니저상태에서 들어갈 수 있고 adminSecret은 관리자만 들어갈 수 있다.
adminSecret에 매니저 상태로 들어가면 에러가 생긴다.
'Spring_boot > Project' 카테고리의 다른 글
[Spring] Spring MVC와 Web Security 통합 (0) | 2021.08.29 |
---|---|
[Spring] 자바 환경 변수 설정하기 (0) | 2021.08.26 |
[Spring] 스프링부트(Spring Tool suite) 설치 방법 (0) | 2021.08.22 |
[Spring] REST 방식의 댓글 처리와 JPA 처리 (0) | 2021.08.19 |
[Spring] 웹 애플리케이션 제작 (0) | 2021.08.16 |