본문 바로가기
Spring/Reids

[Spring Interceptor] Interceptor에서 Redis-Session 정보를 이용하여 유저 권한 체크

by 행운의나무 2021. 8. 27.
728x90
반응형

Redis 관련 설정 포스트 :

2021.08.27 - [Spring/Reids] - [Spring Redis-Session] Spring Redis Session With Docker

 

[Spring Redis-Session] Spring Redis Session With Docker

Docker를 이용하여 Reids 서버 구동 도커를 재시작했을 때, redis-cli에 다시 접속 : docker exec -it dockerRedis redis-cli docker pull redis docker network create redis-net #dockerRedis라는 이름의 컨테이..

twer.tistory.com

Interceptor 간단한 예제 포스트 :

2021.08.27 - [Spring] - [Spring Interceptor] 커스텀 어노테이션과 Intercepter 구현

 

[Spring Interceptor] 커스텀 어노테이션과 Intercepter 구현

목표 : 커스텀 어노테이션을 만들고, Interceptor에서 어노테이션 여부를 확인한다. Spring Interceptor Spring 영역 안에서 Controller가 실행 되기 전, 후 처리에 대한 기능들을 적용한다. Spring 영역 안에서..

twer.tistory.com

모델 추가

  • User
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class User {

    private String name;

    private int age;
}
  • SessionMember : 세션 이용에서 직렬화/역직렬화를 위해 User의 Dto 역할을 하는 클래스를 따로 둔다.
@Data
@NoArgsConstructor
@AllArgsConstructor
public class SessionMember implements Serializable {

    private String name;

    private int age;
}

커스텀 어노테이션 추가

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Auth {

}

인터셉터 추가

@Slf4j
@Component
public class SessionAuthInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //어노테이션 체크 - Controller에 @Auth 어노테이션이 있는지 확인

        boolean hasAnnotation = checkAnnotation(handler, Auth.class);

        if (hasAnnotation) {

            //어노테이션이 있으면서, User의 정보가 맞다면 true 반환
            //request에서 session 받아오기
            HttpSession session = request.getSession();
            SessionMember sessionMember = (SessionMember) session.getAttribute("sessionMember");//sessionMember객체로 저장된 객체 반환
            String userName = sessionMember.getName();
            Integer userAge = sessionMember.getAge();

            log.info("userName, userAge : {}, {}", userName, userAge);

            //User의 정보는 DB에서 불러오지만, 여기서는 간단히 하기 위해 임의의 값으로 확인
            // walter와 20이 세션정보에 있을 때 권한이 있는것으로 가정

            if (userName.equals("walter") && userAge.equals(20)) {
                return true;
            }

            throw new AuthException();
        }

        //Auth를 실패하더라도 Controller를 실행하기 위해서는 true로 설정해야한다. ex)/session/add의 경우 walter/20 이 아닌 다른 값이 들어가도 실행되어야 한다.
        return true;
    }


    private boolean checkAnnotation(Object handler, Class<Auth> authClass) {
        //js. html 타입인 view 과련 파일들은 통과한다.(view 관련 요청 = ResourceHttpRequestHandler)
        if (handler instanceof ResourceHttpRequestHandler) {
            return true;
        }

        HandlerMethod handlerMethod = (HandlerMethod) handler;

        //Auth anntotation이 있는 경우
        if (null != handlerMethod.getMethodAnnotation(authClass) || null != handlerMethod.getBeanType().getAnnotation(authClass)) {
            return true;
        }

        //annotation이 없는 경우
        return false;
    }

}

예외 처리 설정

  • @RestControllerAdvice로 전역 예외처리 가능
public class AuthException extends RuntimeException{

    public AuthException(){
        super(HttpStatus.UNAUTHORIZED.toString());
    }
}

@RestControllerAdvice
public class ExceptionAdviceHandler {

    @ResponseStatus(HttpStatus.UNAUTHORIZED)
    @ExceptionHandler(AuthException.class)
    public ResponseEntity authException(walter.unit.interceptor_reids_session.exception.AuthException e){
        return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("권한이 없습니다. " + e.getLocalizedMessage());
    }
}

인터셉터 설정

@RequiredArgsConstructor
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {

    private final SessionAuthInterceptor sessionAuthInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(sessionAuthInterceptor).addPathPatterns("/session/*");
    }
}

Controller

@Slf4j
@Controller
public class SessionInterceptorController {

    @GetMapping("/session")
    public String index(){
        return "sessionAdd";
    }

    @PostMapping("/session/add")
    public ResponseEntity add(@RequestBody User user, HttpSession session){
        log.info("user: {}", user);
        SessionMember sessionMember = new SessionMember();
        sessionMember.setName(user.getName());
        sessionMember.setAge(user.getAge());

        session.setAttribute("sessionMember", sessionMember);

        return ResponseEntity.ok().body("session add");
    }

    @Auth
    @GetMapping("/session/auth")
    public String sessionAuth(HttpSession httpSession, Model model){
        model.addAttribute("userData", (SessionMember)httpSession.getAttribute("sessionMember"));
        return "sessionResult";
    }

    @GetMapping("/session/non-auth")
    public String sessionNonAuth(HttpSession httpSession) {
        return "sessionResult";
    }

    @GetMapping("/session/logout")
    public String logout(HttpSession httpSession){
        httpSession.invalidate();
        return "sessionAdd";
    }
}

View

  • Mustache 이용
  • sessionAdd.mustache
<html>

<head>
    <title> 세션 추가 페이지 </title>
</head>

<body>

<form>
    name : <input type="text" id="name">
    age : <input type="text" id="age">
    <input type="button" id="submitButton" value="추가">
</form>

</body>
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="/js/add.js"></script>
</html>
  • sessionResult.mustache
<html>

<head>
    <title> 세션 유저 정보 확인 페이지 </title>
</head>

<body>
{{#userData}}
    userName : {{name}} <br>
    userAge : {{age}}
{{/userData}}

<!-- userData가 없을 때 ex)/session/non-auth -->
{{^userData}}
    유저 정보가 없습니다.
{{/userData}}

</body>
</html>
  • add.js
var add = {

    init:function(){
             var _this = this;
    $('#submitButton').on('click', function(){
                _this.submit();
             });
    },

    submit:function(){
            var user = {
                 name : document.getElementById("name").value,
                 age : document.getElementById("age").value
            };

            $.ajax({
                  type: 'POST',
                  url: '/session/add',
                  dataType: 'text',
                  contentType:'application/json; charset=utf-8',
                  data: JSON.stringify(user),
                  success:function(data){
                       console.log("성공");
                       window.location.href="/session/auth";
                  },
                  error:function(e){
                    console.log("실패");
                    alert(e.status);
                  }

                });
            },
};

add.init();

테스트

  • http://localhost:8080/session 에서 name, age 전송 => 로그인 로직을 단순화시켜 walter, 20을 넣으면 통과하도록 인터셉터 로직이 되어 있다.
    • [reids] flushall : 모든 데이터 지우기
    • [browser] name, age 폼 입력 후 추가
  • http://localhost:8080/session/auth 접속 시
    • testuser / 10 => 예외 발생
      • [redis] keys *
      • redis-session
      • [browswer] 
        Exception 발생

    • walter / 20 => 성공 => 결과화면
      • [browser] localhost:8080/session => walter / 20 입력
      • [redis] keys * => 세션 정보가 업데이트된다.
        업데이트 된 Redis-Session
      • [browser] Intercepter를 성공적으로 통과
  • http://localhost:8080/session/logout
    • redis에 key 출력 => 세션정보 삭제 확인

Github : https://github.com/twer4774/interceptor_reids_session

쿠팡으로 연결 클릭

 

제주삼다수 그린

COUPANG

www.coupang.com

파트너스 활동을 통해 일정액의 수수료를 제공받을 수 있음

반응형

'Spring > Reids' 카테고리의 다른 글

[Spring Redis-Session] Spring Redis Session With Docker  (0) 2021.08.27