3月30日
67476 ワード
きょう習った
セキュリティの使用
package com.myapp.pma.entities;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
//테이블 자동생성을 사용하지 않고 DB에 테이블을 직접 생성해서 클래스 이름과 다르기 때문에 적어줘야함)
@Table(name = "user_accounts")
public class UserAccount {
@Id // 자동증가
@GeneratedValue(strategy = GenerationType.IDENTITY) // DB에서 자동으로 생성
@Column(name = "user_id") // 테이블의 Column 이름과 변수명이 다르기 때문에 일치시키기 위해 적어줘야함
private long userId;
@Column(name = "username") // 테이블의 Column 이름과 변수명이 다르기 때문에 일치시키기 위해 적어줘야함
private String userName;
private String email;
private String password;
private String role = "ROLE_USER";
private boolean enabled = true;
public UserAccount() {
// 빈 유저 객체 생성시 role은 유저, enable은 true
this.role = "ROLE_USER";
this.enabled = true;
}
public long getUserId() {
return userId;
}
public void setUserId(long userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
}
登録する<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link
href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3"
crossorigin="anonymous"
/>
<title>시큐리티</title>
</head>
<body style="background-color: #ededed">
<div style="background-color: #337ab7; height: 50px"></div>
<div class="container-fluid" style="margin-top: 30px">
<div class="row col-lg-4 mx-auto" style="margin-top: 40px; background-color: #fff; padding: 20px; border: solid 1px #ddd">
<form autocomplete="off" th:action="@{/register/save}" th:object="${userAccount}" method="post" class="form-signin" role="form">
<h3 class="form-signin-heading">가입하기 FORM</h3>
<div class="form-group">
<div class="mb-2">
<input type="text" th:field="*{userName}" placeholder="유저이름" class="form-control" />
</div>
</div>
<div class="form-group">
<div class="mb-2">
<input type="text" th:field="*{email}" placeholder="이메일" class="form-control" />
</div>
</div>
<div class="form-group">
<div class="mb-2">
<input type="password" th:field="*{password}" placeholder="패스워드" class="form-control" />
</div>
</div>
<!-- thymeleaf를 사용하면 아래의 것은 필요 없음-->
<!-- <input type="hidden" name="_csrf" th:value="${_csrf.token}"> -->
<div class="form-group">
<div class="d-grid mb-2">
<button type="submit" class="btn btn-primary">가입하기</button>
</div>
</div>
<!-- <span th:utext="${successMessage}"></span> -->
</form>
</div>
</div>
</body>
</html>
package com.myapp.pma.controllers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import com.myapp.pma.dao.UserAccountRepository;
import com.myapp.pma.entities.UserAccount;
@Controller
public class SecurityController {
@Autowired
private UserAccountRepository userRepo;
// 암호화는 저장할 때와 인증할때 필요함
@Autowired
private BCryptPasswordEncoder bEncoder;
// 가입하기 화면 표시
@GetMapping("/register")
public String register(Model model) {
UserAccount userAccount= new UserAccount();
model.addAttribute("userAccount", userAccount);
return "security/register";
}
@PostMapping("/register/save")
public String saveUser(Model model, UserAccount user) {
//비밀번호 암호화 후 저장
// 넘어온 user정보에서 비밀번호만 얻어서 BCryptPasswordEncoder로 인코딩한 후 저장
user.setPassword(bEncoder.encode(user.getPassword()));
userRepo.save(user);
return "redirect:/";
}
}
package com.myapp.pma.security;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
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.bcrypt.BCryptPasswordEncoder;
// Security 사용순서
//1. Security 설정을 위해서 WebSecurityConfigurerAdapter 상속 받음
//2. 어노테이션 @EnableWebSecurity
@EnableWebSecurity
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
private DataSource dataSource; // MySQL DB와 연결
@Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder; // 패스워드 인코딩 객체
@Override // 3. 인증
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// 메모리에 아이디, 비밀번호, 역할(권한)설정
auth.jdbcAuthentication()
.usersByUsernameQuery("select username,password,enabled from user_accounts where username = ? ") // DB에 있는지 확인
.authoritiesByUsernameQuery("select username,role from user_accounts where username = ? ")
.dataSource(dataSource)
.passwordEncoder(bCryptPasswordEncoder); // 암호화된 패스워드를 디코딩해서 입력된 비밀번호와 비교
}
@Override // 4. 허가
protected void configure(HttpSecurity http) throws Exception {
http.authorizeHttpRequests()
.antMatchers("/projects/new").hasRole("ADMIN") // 새 프로젝트는 관리자만
.antMatchers("/projects/save").hasRole("ADMIN")
.antMatchers("/employees/new").hasRole("ADMIN") // 새 직원은 관리자만
.antMatchers("/employees/save").hasRole("ADMIN")
.antMatchers("/employees/").authenticated() // 인증된 유저
.antMatchers("/projects/").authenticated()
.antMatchers("/","/**").permitAll() // 누구든 접근 가능
.and()
.formLogin(); // 로그인창 사용
// Security에서는 기본적으로 csrf 방지가 적용중
// http.csrf().disable(); // 사용자 의도치 않게 행동하는 것을 방지, save 후 redirect 하는 과정에서 csrf 룰에 위배되어 에러 출력
}
}
Errorページの設定
application.propertiesでデフォルト設定されているエラーページを無効にする
server.error.whitelabel.enabled=false
エラー発生時に出力されるコードを受信し、コードに応じて異なるエラーページを移動
package com.myapp.pma.controllers;
import javax.servlet.RequestDispatcher;
import javax.servlet.http.HttpServletRequest;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class AppErrorController implements ErrorController {
// 에러 발생시 주소가 "/error"로 들어온다.
@GetMapping("/error")
public String handleError(HttpServletRequest request) {
// 에러 상태 코드 확인
Object status = request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
if (status != null) { // 에러가 맞을 시
Integer statusCode = Integer.valueOf(status.toString()); // 403, 404, 500
if (statusCode == HttpStatus.NOT_FOUND.value()) {
return "errorpages/404";
}
else if (statusCode == HttpStatus.INTERNAL_SERVER_ERROR.value()) {
return "errorpages/500";
}
else if (statusCode == HttpStatus.FORBIDDEN.value()) {
return "errorpages/403";
}
}
// 위의 에러상태가 아닐 경우 그냥 error 페이지로 이동
return "errorpages/error";
}
}
Loginページの設定
.formLogin(form -> form.loginPage("/login")
.permitAll()) // 커스텀 로그인페이지 사용
.logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout")); // 로그아웃 추가, 로그인 페이지를 새로 만들면 로그아웃도 새로 설정
templatesフォルダにログインします.htmlファイルの生成
認証が必要なページではなく、カスタムログインページにアクセスします.
navbarの変更
購読、ログインボタンの追加
ログイン時に認証するユーザのユーザ名とログアウトボタン出力
<ul class="navbar-nav me-5" th:if="${principal == null}">
<li class="nav-item">
<a class="nav-link active" th:href="@{/register}">가입하기</a>
</li>
<li class="nav-item">
<a class="nav-link active" th:href="@{/login}">로그인</a>
</li>
</ul>
<form th:if="${principal != null}" method="post" th:action="@{/logout}">
<span class="text-white" th:text="${'Hi🥇, ' + principal}"></span>
<button class="btn btn-secondary">로그아웃</button>
</form>
package com.myapp.pma;
import java.security.Principal;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ModelAttribute;
@ControllerAdvice // 모든 Controller에 적용(모든 주소에 적용)
public class Common {
// 각 Controller가 화면(뷰)에 보내는 Model객체에 추가
@ModelAttribute
public void sharedData(Model model, Principal principal) {
// Principal은 Security인증시 인증된 유저정보를 담고 있는 객체
if (principal != null) {
model.addAttribute("principal", principal.getName()); // 인증 유저의 유저네임을 전달
}
}
}
Reference
この問題について(3月30日), 我々は、より多くの情報をここで見つけました https://velog.io/@tutu10000/3월-30일テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol