[spring boot] 게시판 스프링 시큐리티와 OAuth2.0으로 로그인 기능 구현 - 03

브라우저에서 로그인 테스트

index.mustache 추가 작성

{{>layout/header}}

<h1>스프링부트로 시작하는 웹 서비스 Ver.2</h1>
<div class="col-md-12">
    <div class="row">
        <div class="col-md-6">
            <a href="/posts/save" role="button" class="btn btn-primary">글 등록</a>
            {{#loginuserName}}
                Logged in as: <span id="user">{{loginuserName}}</span>
                <a href="/logout" class="btn btn-info active" role="button">Logout</a>
            {{/loginuserName}}
            {{^loginuserName}}
                <a href="/oauth2/authorization/google" class="btn btn-success active" role="button">Google Login</a>
            {{/loginuserName}}
        </div>
    </div>
    <br>
    <!-- 목록 출력 영역 -->
    <table class="table table-horizontal table-bordered">
        <thead class="thead-strong">
        <tr>
            <th>게시글번호</th>
            <th>제목</th>
            <th>작성자</th>
            <th>최종수정일</th>
        </tr>
        </thead>
        <tbody id="tbody">
        {{#posts}}
            <tr>
                <td>{{id}}</td>
                <td><a href="/posts/update/{{id}}">{{title}}</a></td>
                <td>{{author}}</td>
                <td>{{modifiedDate}}</td>
            </tr>
        {{/posts}}
        </tbody>
    </table>
</div>

{{>layout/footer}}
  • {{#userName}}, {{/userName}}
    머스테치에서는 if문을 제공하지 않습니다.
    true/false 여부만 판단 합니다.
    userName이 있다면 userName을 노출 시키는 구문

  • a href="/logout"
    스프링 시큐리티에서 기본적으로 제공하는 로그아웃 URL
    별도로 로그아웃 api를 컨트롤러에 작성하지 않아도됨
    앞서 작성한 SecurityConfig클래스에서 URL을 변경할 수 있음

  • {{^userName}}, {{/userName}}
    머스테치에서 userName값이 존재하지 않을때 로그인 버튼을 노출시키는 구문

  • a href="/oauth2/authorization/google"
    스프리 시큐리티에서 기본적으로 제공하는 로그인 URL
    로그아웃과 마찬가지로 api를 생성하지 않아도됨

IndexController 추가 작성

package com.momenting.book.springboot.web;

import com.momenting.book.springboot.config.auth.dto.SessionUser;
import com.momenting.book.springboot.service.posts.PostsService;
import com.momenting.book.springboot.web.dto.PostsResponseDto;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

import javax.servlet.http.HttpSession;

@RequiredArgsConstructor
@Controller
public class IndexController {

    private final PostsService postsService;
    private final HttpSession httpSession;

    @GetMapping("/")
    public String index(Model model) {
        model.addAttribute("posts", postsService.findAllDesc());
        SessionUser user = (SessionUser) httpSession.getAttribute("user");
        if (user != null) {
            model.addAttribute("userName", user.getName());
        }
        return "index";
    }

    @GetMapping("/posts/save")
    public String postsSave() {
        return "posts-save";
    }

    @GetMapping("/posts/update/{id}")
    public String postsUpdate(@PathVariable Long id, Model model) {
        PostsResponseDto dto = postsService.findById(id);
        model.addAttribute("post", dto);
        return  "posts-update";
    }

}
  • 이번 포스팅에서 추가 작성된 구문
    private final HttpSession httpSession;

    @GetMapping("/")
    public String index(Model model) {
    model.addAttribute("posts", postsService.findAllDesc());
    SessionUser user = (SessionUser) httpSession.getAttribute("user");
    if (user != null) {
    model.addAttribute("loginuserName", user.getName());
    }
    return "index";
    }

구글 로그인 테스트

Google Login 버튼을 클릭하면

와 같이 구글 로그인 동의 화면으로 이동합니다. 로그인할 계정을 선택하면 로그인이 진행됩니다.

구글프로필에서의 사용자 이름이 노출 됩니다. 하지만 h2데이터베이스에서 확인해 보면

저의 구글 계정정보가 잘 저장된것을 확인할 수 있습니다.

권한 변경하기

권한을 변경하기전에 로그인 후 게시글을 등록하게되면 권한이 GUEST기 때문에 글 등록이 안될것 입니다.
그래서 권한을 USER로 변경해준 후 게시글을 등록한다면 글 등록이 달 될겁니다.

어노테이션 기반으로 개선하기

@LoginUser 어노테이션 생성

  • config.auth 패키지에 생성해 줍니다

  • 작성

package com.momenting.book.springboot.config.auth;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface LoginUser {
}

LoginUserArgumentResolver 생성

  • config.auth 패키지에 생성해 줍니다

  • 작성

package com.momenting.book.springboot.config.auth;

import com.momenting.book.springboot.config.auth.dto.SessionUser;
import lombok.RequiredArgsConstructor;
import org.springframework.core.MethodParameter;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

import javax.servlet.http.HttpSession;

@RequiredArgsConstructor
@Component
public class LoginUserArgumentResolver implements HandlerMethodArgumentResolver {

    private final HttpSession httpSession;

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        boolean isLoginUserAnnotation = parameter.getParameterAnnotation(LoginUser.class) != null;
        boolean isUserClass = SessionUser.class.equals(parameter.getParameterType());
        return isLoginUserAnnotation && isUserClass;
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        return httpSession.getAttribute("user");
    }
}

WebConfig 생성

  • config 패키지에 생성해 줍니다

  • 작성

package com.momenting.book.springboot.config;

import com.momenting.book.springboot.config.auth.LoginUserArgumentResolver;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.List;

@RequiredArgsConstructor
@Configuration
public class WebConfig implements WebMvcConfigurer {
    private final LoginUserArgumentResolver loginUserArgumentResolver;

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        argumentResolvers.add(loginUserArgumentResolver);
    }
}

IndexController 변경

package com.momenting.book.springboot.web;

import com.momenting.book.springboot.config.auth.LoginUser;
import com.momenting.book.springboot.config.auth.dto.SessionUser;
import com.momenting.book.springboot.service.posts.PostsService;
import com.momenting.book.springboot.web.dto.PostsResponseDto;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

import javax.servlet.http.HttpSession;

@RequiredArgsConstructor
@Controller
public class IndexController {

    private final PostsService postsService;

    @GetMapping("/")
    public String index(Model model, @LoginUser SessionUser user) {
        model.addAttribute("posts", postsService.findAllDesc());
        if (user != null) {
            model.addAttribute("loginuserName", user.getName());
            System.out.println("얍얍4" +user.getName());
        }
        return "index";
    }

    @GetMapping("/posts/save")
    public String postsSave() {
        return "posts-save";
    }

    @GetMapping("/posts/update/{id}")
    public String postsUpdate(@PathVariable Long id, Model model) {
        PostsResponseDto dto = postsService.findById(id);
        model.addAttribute("post", dto);
        return  "posts-update";
    }

}

세션 저장소로 데이터베이스 사용하기

의존성 추가

  • build.gradle에 compile('org.springframework.session:spring-session-jdbc') 추가해 줍니다.

application.properties 추가

  • spring.session.store-type=jdbc라는 구문을 추가로 작성해 줍니다.

좋은 웹페이지 즐겨찾기