2022.02.14.Mon.
✍ 복습
spring security : MVC 모드
- 인증 authentication : 로그인
@PreAuthorize("isAnonymous()") 회원가입 페이지
@PreAuthorize("isAuthenticated()") 글쓰기
- 인가 authorization : 권한, 인증된 다음
@Secured("ROLE_USER")
@Secured("ROLE_ADMIN")
@Secured({"ROLE_UESR", "ROLE_ADMIN"})
- 페이지 위조 방지 : csrf
서버에서 페이지에 csrf token을 추가해서 사용자에게 보낸다.
사용자는 csrf token이 불일치하거나 수명이 지났으면 403(권한없음) 처리.
사용자는 csrf token을 되돌린다. <input..............name={"_csrf" value=$(_csrf.token}">
- 인증이나 인가별로 메뉴를 다르게 구성
springSecurityConfig.java
package com.example.demo;
import org.springframework.beans.factory.annotation.*;
import org.springframework.context.annotation.*;
import org.springframework.security.config.annotation.authentication.builders.*;
import org.springframework.security.config.annotation.method.configuration.*;
import org.springframework.security.config.annotation.web.builders.*;
import org.springframework.security.config.annotation.web.configuration.*;
import org.springframework.security.crypto.password.*;
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled=true, securedEnabled=true)
public class SpringSecurityConfig2 extends WebSecurityConfigurerAdapter {
@Autowired
private PasswordEncoder passwordEncoder;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin().loginPage("/sample2/login").loginProcessingUrl("/sample2/login")
.usernameParameter("username").passwordParameter("password")
.defaultSuccessUrl("/").failureUrl("/sample2/login?error")
.and().exceptionHandling().accessDeniedPage("/sample2/error")
.and().logout().logoutUrl("/sample2/logout").logoutSuccessUrl("/");
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("spring").password(passwordEncoder.encode("1234")).roles("USER")
.and()
.withUser("system").password(passwordEncoder.encode("1234")).roles("ADMIN")
.and()
.withUser("admin").password(passwordEncoder.encode("1234")).roles("USER","ADMIN");
}
}
- @EnableWebSecurity
스프링시큐리티 사용을 위한 어노테이션 선언
- @EnableGlobalMethodSecurity(prePostEnabled=true, securedEnabled=true)
스프링 시큐리티의 메소드 어노테이션 기반 시큐리티를 활성화 하기 위해서 필요
securedEnabled = true 로 설정하면 @Secured 어노테이션 사용 가능
- WebSecurityConfigurerAdapter : 스프링 시큐리티 설정을 기본 구현한 중간 단계 추상 클래스
- public class SpringSecurityConfig2 extends WebSecurityConfigurerAdapter
WebSecurityConfigurerAdapter을 상속
- protected void configure(HttpSecurity http) throws Exception {
http
WebSecurityconfigurerAdapter가 제공, 사용자 인증이 된 요청에 대해서만 요청을 허용, 사용자는 폼기반 로그인으로 인증할 수 있다. 사용자는 HTTP 기반 인증으로 인증할 수 있다.
- .FormLogin()
화면을 보여주고 아이디와 비밀번호를 입력하는 전통적인 로그인화면 - csrf 자동 활성화
- .loginPage()
로그인 페이지를 보여줄 주소(get)
- .loginProcessingUrl()
로그인을 처리할 주소(post)
- .usernameParameter()
login.html에서 아이디를 입력받을 name
- .passwordParameter()
login.html에서 비밀번호를 입력받을 name
- .defaultSuccessUrl()
로그인에 성공하면 어느 페이지로 이동?
- .failureUrl()
로그인에 실패하면 어느 페이지로 이동?
- http.exceptionHandling().accessDeniedPage("/sample2/error");
권한 오류 403에 대한 설정으로 403이 발생하면 /sample/error로 이동해라
- .logout()
logout 관련 설정을 진행할 수 있도록 돕는 LogoutConfigurer<> class를 반환
- .logoutUrl()
client에서 SpringSecurity에게 logout을 요청하기 위한 url을 설정하는 메소드
- .logoutSuccessUrl()
로그아웃 성공 시 사용자가 redirect될 url을 지정하는 메소드
- AuthenticationManagerBuilder
AuthenticationManagerBuilder를 통해 인증 객체를 만들 수 있도록 제공 즉, 사용자 아이디, 비밀번호, 권한등을 관리
- 테스트에 사용할 사용자를 생성 user, sys, admin 각 사용자별로 권한을 설정.
springSecurityController.java
package com.example.demo.controller;
import org.springframework.security.access.annotation.*;
import org.springframework.security.access.prepost.*;
import org.springframework.stereotype.*;
import org.springframework.web.bind.annotation.*;
@Controller
public class SampleController2 {
@PreAuthorize("isAnonymous()")
@GetMapping("/sample2/login")
public void login() {
}
@GetMapping({"/", "/sample2/list"})
public String list() {
return "sample2/list";
}
@Secured("ROLE_USER")
@GetMapping("/sample2/user")
public void user() {
}
@Secured("ROLE_ADMIN")
@GetMapping("/sample2/admin")
public void admin() {
}
@PreAuthorize("isAuthenticated()")
@GetMapping("/sample2/authenticated")
public void authenticated() {
}
@PreAuthorize("isAnonymous()")
@GetMapping(value="/sample2/anonymous")
public void anonymous() {
}
@GetMapping(value="/sample2/error")
public void error403() {
}
}
- @PreAuthorize(),@Secured()
권한 설정이 필요한 위치에 어노테이션을 추가해 주면 권한 별로 접근을 통제할 수 있다.
login.html 로그인 폼
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" xmlns:th="http://www.thymeleaf.org">
<title>Insert title here</title>
</head>
<body>
<div th:text="${_csrf.token}"></div>
<form action="/sample/login" method="post">
아이디:<input type="text" name="username" value="spring">
비밀번호:<input type="password" name="password" value="1234">
<input type="hidden" name="_csrf" th:value="${_csrf.token}">
<button>로그인</button>
</form>
</body>
</html>
nav.html 메뉴
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<title>Insert title here</title>
<script>
$(function() {
$('#login').click(function() {
location.href = "/sample2/login";
});
$('#logout').click(function() {
const $form = $('<form>').attr('action','/sample2/logout').attr('method','post').appendTo($('body'));
$('<input>').attr('type','hidden').attr('name','_csrf').val($('#csrf').text()).appendTo($form);
$form.submit();
});
});
</script>
</head>
<body>
<span th:text='${_csrf.token}' id="csrf"></span>
<button sec:authorize="isAnonymous()" id="login">로그인</button>
<button sec:authorize="isAuthenticated()" id="logout">로그아웃</button>
<div sec:authorize="hasRole('ADMIN')">권한 : 어드민 유저</div>
<div sec:authorize="hasRole('USER')">권한 : 일반 유저</div>
</body>
</html>