OAuth2 인증 테스트를 진행할 때 grant typepassword로 설정하여 접속 하는 경우 필요한 파라미터는 grant_type = password, client_id, client_secret, username, password 입니다. 테스트를 진행하다 보면 Unsupported grant type: password 라는 리턴 값을 받는 경우가 있습니다.

{

"error":"unsupported_grant_type",

"error_description":"Unsupported grant type: password"

}

 

실제 리턴되는 형태는

oauth2 grant type password

 

org.springframework.web.client.HttpClientErrorException$BadRequest: 400 :

[{"error":"unsupported_grant_type","error_description":"Unsupported grant type: password"}]

 

일단 오류 결과를 얻었다는 건 OAuth2 Server 작업은 잘 되었다고 볼 수 있습니다그럼 이제 오류만 해결하면 됩니다사실 이건 오류는 아니고 설정이 빠져서 발생하는 부분입니다.


AuthorizationServerConfigurerAdapter를 상속받아 서버 인증 설정을 하게 되는데 상속하는 Method에서 configure(AuthorizationServerEndpointsConfigurer endpoints) 작업을 하지 않은 경우일 가능성이 있습니다해결책은 여기에 AuthenticationManager를 추가해 주면 됩니다.


WebSecurityConfigurerAdapter에 다음과 같이 Bean을 설정 하고

@Configuration

@EnableWebSecurity

public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

 

    @Override

    @Bean

    public AuthenticationManager authenticationManagerBean() throws Exception {

        return super.authenticationManagerBean();

    }

}

 

 

AuthorizationServerConfig endpoints 추가를 해줍니다.

@Configuration

@EnableAuthorizationServer

public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

 

   @Override

   public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {

 

          endpoints.authenticationManager(authenticationManager);

   }

 

 }

 

 

그리고 다시 테스트를 하면 원하는 결과를 얻을 수 있습니다.


oauth2 grant type password

oauth2 grant type password


- copy coding -


아침에 일어나서 작업을 하려는데 컴퓨터가 잘 작동을 하지 않습니다작업 자체가 어렵고 마우스 조차도 제대로 움직이지 않습니다무슨 일인지 일단 Windows 작업 관리자를 열어서 성능 탭을 확인 해 봅니다.


stsess 삭제 제거


이게 뭐야....

이번에는 프로세스 탭으로 이동합니다.


stsess 삭제 제거


StSess.exe 프로세스가 50이상 올라갔다 내려갔다 하는군요.  

 


실패한 방법

 

평상시 하던 대로 프로세스를 선택하고 우측 하단 프로세스 끝내기 버튼을 클릭합니다.


stsess 삭제 제거


다시 팝업 창에서 프로세스 트리 끝내기 버튼을 클릭해 봅니다.


stsess 삭제 제거


안되는 군요.


이번에는 마우스 우측 버튼을 이용하여 프로세스 트리 끝내기를 선택 해도 오류가 발생 합니다.


stsess 삭제 제거


대부분 이렇게 하면 종료가 되는데 이건 이런식으로 중단이 되지 않도록 되어 있습니다.

 

 

성공한 방법

 

그럼 제어판을 이용하여 작업을 진행 합니다. 제어판을 열고


stsess 삭제 제거


프로그램 및 기능을 선택 합니다.


stsess 삭제 제거


이곳에 AhnLab Safe Transaction 프로그램이 보이는 군요. 더블 클릭을 하거나 상단에 제거 버튼을 클릭 합니다

설치한 기억이 없겠지만 인터넷으로 은행 업무등을 할때 설치된 것으로 삭제를 하면 나중에 은행 업무등을 진행 할때 다시 설치가 됩니다.


stsess 삭제 제거


그냥 다음 버튼을 클릭 합니다.


stsess 삭제 제거


이번에는 보이는 숫자를 입력하고 제거 버튼을 클릭 합니다.


stsess 삭제 제거


제거가 진행 되는데 컴퓨터가 거의 먹통이니 다른 작업을 할 수 없어 자세히 보기 버튼을 클릭해 보았습니다.


stsess 삭제 제거


이걸 보고 있어야 하나.... 잠시 딴짓...


stsess 삭제 제거


제거가 완료 되었습니다.


stsess 삭제 제거


이제 널널하게 사용 가능 합니다


- copy coding -


SpringSecurity를 추가하면 자동적으로 로그인은 내장된 화면을 이용하여 진행하도록 되어 있습니다.


spring security login view


이 화면이 심플해서 맘에 들어 그냥 사용하고 싶다면 괜찮지만 새로 만드는 작업이 그리 복잡하지 않기 때문에 가능하면 직접 만들어 사용 하는 것도 좋습니다한번 프로젝트를 생성해서 로그인 화면을 작성해 보겠습니다.


spring security login view


테스트를 위해 프로젝트 이름을 Login 으로 생성 했습니다.


spring security login view


추가되는 라이브러리는 Security Web입니다.

 

그 외에 필요한 라이브러리는 pom.xml에 직접 추가를 해줍니다.

pom.xml

                <dependency>

                       <groupId>org.apache.tomcat.embed</groupId>

                       <artifactId>tomcat-embed-jasper</artifactId>

               </dependency>

               <dependency>

                   <groupId>org.webjars</groupId>

                   <artifactId>jquery</artifactId>

                   <version>3.4.1</version>

               </dependency>

               <dependency>

                   <groupId>javax.servlet</groupId>

                   <artifactId>jstl</artifactId>

                   <version>1.2</version>

               </dependency>


Login 화면용 jsp 파일의 위치를 정하기 위해 application.properties 파일에 설정을 해주고

 #jsp location

spring.mvc.view.prefix=/WEB-INF/jsp/

spring.mvc.view.suffix=.jsp

server.servlet.jsp.init-parameters.development=true


설정한 값을 이용하여 파일이 들어갈 폴더도 생성 합니다.


spring security login view


여기 까지는 일반 프로젝트를 만드는 방법과 동일한 작업 입니다이제 단 한가지 작업만 진행 하면 로그인 화면을 직접 만들어 사용할 수 있습니다.

기본 로그인 화면을 새로 생성한 로그인 페이지를 이용 하도록 수정하려면 WebSecurityConfigurerAdapter를 상속받은 class를 생성해서 configure(HttpSecurity http)에 설정을 추가 하기만 하면 됩니다.


         @Override

        protected void configure(HttpSecurity http) throws Exception {

               http.httpBasic().and().authorizeRequests()

        .antMatchers("/user/login").permitAll()

        .and().logout().permitAll()

        .and().formLogin()

        .loginPage("/user/login")

        .loginProcessingUrl("/loginProcess")

        .defaultSuccessUrl("/mainHome")

        .and().csrf().disable();

        }


위의 설정은

loginPage("/user/login") : 새로운 로그인 페이지 호출을 설정 합니다.

loginProcessingUrl("/loginProcess") : 실제 로그인을 진행 합니다.

이 두 가지만 해주면 설정은 끝입니다.

defaultSuccessUrl("/mainHome") 이건 그냥 로그인 성공시 보여줄 페이지 입니다.

 

실제로 호출되는 Controller 클래스도 생성을 합니다.


 @RequestMapping("/user/login")

public String login() throws Exception {

        return "login/userLogin";

}

       

@RequestMapping("/mainHome")

public String loginMain() throws Exception {

        return "main";

}


작동 순서

1. 로그인 페이지 설정 :

로그인을 진행할 페이지를 호출하기 위해 Controller에서 @RequestMapping("/user/login") 요렇게 해놓고 return "login/loginForm"; 리턴에서 새로 생성한 로그인 화면(loginForm.jsp) 보여줍니다.

2. 로그인 진행 :

/login/LoginForm.jsp 에서 <form> 태그에 username, password를 입력하고

<form action="/loginProcess" method="post"> 이렇게 처리하면 로그인을 진행 합니다.

3. 로그인 성공 페이지

로그인이 완료 되면 Controller에서 @RequestMapping("/mainHome") 이걸 호출 하고 return "main";으로 넘겨  main.jsp 화면을 보여 줍니다.

 

신규 로그인 화면 입니다.


spring security login view


기본 제공 로그인 화면이 더 좋은거 같군요...

 

다음은 로그인 성공시 보여주는 화면입니다.


spring security login view


예쁘게 꾸며서 사용하세요.


전체 소스


WebSecurityConfigurerAdapter

package com.copycoding.security;

 

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;

import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;

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;

import org.springframework.security.crypto.password.PasswordEncoder;

 

@Configuration

@EnableWebSecurity

public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

 

        @Override

        protected void configure(HttpSecurity http) throws Exception {

               http.httpBasic().and().authorizeRequests()

        .antMatchers("/user/login").permitAll()

.and().logout().permitAll()

        .and().formLogin()

        .loginPage("/user/login")

        .loginProcessingUrl("/loginProcess")

        .defaultSuccessUrl("/mainHome")

        .and().csrf().disable();

        }

       

        @Autowired

        public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {

               auth.inMemoryAuthentication()

                       .withUser("copycoding").password(passwordEncoder().encode("copycopy")).roles("ADMIN");

               auth.inMemoryAuthentication()

                       .withUser("honggil").password(passwordEncoder().encode("hoho")).roles("USER");

        }

       

        @Bean

    public PasswordEncoder passwordEncoder() {

        return new BCryptPasswordEncoder();

    }

} 


Controller

 package com.copycoding.security;

 

import org.springframework.web.bind.annotation.RequestMapping;

 

public class TestController {

 

@RequestMapping("/user/login")

    public String login() throws Exception {

               return "login/userLogin";

}

       

@RequestMapping("/mainHome")

    public String loginMain() throws Exception {

               return "main";

}

}


/login/loginForm.jsp

 <%@ page language="java" contentType="text/html; charset=EUC-KR"

    pageEncoding="EUC-KR"%>

<!DOCTYPE html>

<html>

<head>

<meta charset="EUC-KR">

<title>Insert title here</title>

</head>

<body>

    <h1>로그인 페이지</h1>

   

<form action="/loginProcess" method="post">

  <table>

<tr>

        <td>username</td><td><input type="text" name="username" placeholder="username 입력"></td>

      </tr>

      <tr>

        <td>password</td><td><input type="password" name="password" placeholder="비밀번호 입력"></td>

      </tr>

      <tr>

        <td colspan="2" align="right"><button type="submit">로그인</button></td>

      </tr>

  </table>

</form>

</body>

</html>


/main.jsp

 <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>

 

<!DOCTYPE html>

<html>

<head>

<meta charset="EUC-KR">

<title>Insert title here</title>

</head>

<body>

 

<h1>Main Page!</h1>

 

</body>

</html>


- copy coding -


Spring Security에서의 권한 별 접근은 아래 source 처럼 대부분 configure(HttpSecurity http)에 설정을 하여 관리를 하게 됩니다.


   @Override

   protected void configure(HttpSecurity http) throws Exception {

      http.httpBasic().and().authorizeRequests()

        .antMatchers("/").permitAll()

        .antMatchers("/page2").hasRole("ADMIN")

        .antMatchers("/page1").hasRole("USER")

        .anyRequest().authenticated()

        .and().logout().permitAll()

        .and().formLogin()

        .and().csrf().disable();

  }


다른 방법을 찾아보면 Controllerannotation을 추가하여 관리하는 방법도 있는데 이것이 @PreAuthorize, @PostAuthorize, @Secured 입니다사용 방법도 너무나 간단한데 권한 설정이 필요한 위치에 @PreAuthorize("hasRole('ROLE_ADMIN')") 이런식으로 어노테이션을 추가해 주면 권한 별로 접근을 통제 하게 됩니다예를 들자면...


    @PreAuthorize("hasRole('ROLE_ADMIN')")

    @RequestMapping("/preRole1")

    public @ResponseBody String preRole1() throws Exception {

              

           return "@PreAuthorize : get role ROLE_ADMIN";

    }


위에있는 예제처럼 추가를 하였는데 아무런 작동을 하지 않는다면 @PreAuthorize @Secured의 사용은 처음에는 비활성화 되어 있어서 사용 하려면 설정을 해줘야 됩니다이것도 아주 간단 한데 Configure 클래스 파일 상단에 아래 코드를 추가해 주기만 하면 활성화 됩니다.

@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)



테스트


몇가지 예를 들어 간단하게 테스트를 해보겠습니다.

 

copycodingADMIN 권한이고 honggil이는 USER 권한을 가지도록 configure에 설정을 합니다.


 @Autowired

 public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {

    auth.inMemoryAuthentication()

        .withUser("copycoding").password(passwordEncoder().encode("copycopy")).roles("ADMIN");

    auth.inMemoryAuthentication()

        .withUser("honggil").password(passwordEncoder().encode("hoho")).roles("USER");

 }


Method에는 annotation을 사용하여 각 권한 별로  접근 설정을 합니다.


 @PreAuthorize("hasRole('ROLE_ADMIN')")

 @RequestMapping("/preRole1")

 public @ResponseBody String preRole1() throws Exception {

              

              return "@PreAuthorize : get role ROLE_ADMIN";

 }

       

 @PreAuthorize("hasRole('ROLE_USER')")

 @RequestMapping("/preRole2")

 public @ResponseBody String preRole2() throws Exception {

              

               return "@PreAuthorize : get role ROLE_USER";

 }

       

 @Secured("ROLE_ADMIN")

 @RequestMapping("/secRole1")

 public @ResponseBody String secRole1() throws Exception {

              

               return "@Secured : get role ROLE_ADMIN";

 }

 

 @Secured("ROLE_USER")

 @RequestMapping("/secRole2")

 public @ResponseBody String secRole2() throws Exception {

              

               return "@Secured : get role ROLE_USER";

 }



@PreAuthorize 테스트


ADMIN 권한이 있는 copycoding으로 로그인을 진행 합니다.


Spring Security PreAuthorize Secured


로그인이 성공 하면 http://localhost:9090/preRole1 에 접속해 봅니다


Spring Security PreAuthorize Secured


접근 권한이 있으니 접근을 할 수 있습니다.


그러면 이번엔 http://localhost:9090/preRole2 에 접속 합니다.


Spring Security PreAuthorize Secured


ADMIN만 접근 가능하니 USER 권한으로는 접근이 불가능 합니다


 

@Secured 테스트

 

USER 권한이 있는 honggil로 로그인을 하고 http://localhost:9090/secRole1 를 호출 합니다.


Spring Security PreAuthorize Secured


ADMIN만 접근 권한이 있으므로 접속이 차단 됩니다.

 

이번엔 http://localhost:9090/secRole2에 접속하면


Spring Security PreAuthorize Secured


USER 권한을 가지고 있어 접속이 가능 합니다.


configure를 이용하는 방법과 annotation을 이용하는 방법의 차이는 접근 권한을 한곳에서 관리 하느냐 여러곳에서 필요에 따라 관리하느냐 하는 관리적인 관점의 차이인데 프로젝트에 따라서 선택을 하던가 적절히 섞어서 사용 하던가 하면 될것 같습니다.

 

@PreAuthorize 와 쌍으로 @PostAuthorize 도 있습니다차이점은 @PreAuthorize 는 말 그대로 실행 전에 권한을 검사하는 거고 @PostAuthorize 는 실행 후 권한을 검사하는 건데 이것도 필요에 따라 선택을 하면 되겠죠.


전체 소스

WebSecurityConfigurerAdapter

package com.copycoding.security;

 

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;

import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;

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;

import org.springframework.security.crypto.password.PasswordEncoder;

 

@Configuration

@EnableWebSecurity

@EnableGlobalMethodSecurity(securedEnabled=true, prePostEnabled=true)

public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

 

        @Override

        protected void configure(HttpSecurity http) throws Exception {

               http.httpBasic().and().authorizeRequests()

        .antMatchers("/").permitAll()

        .antMatchers("/page2").hasRole("ADMIN")

        .antMatchers("/page1").hasRole("USER")

        .anyRequest().authenticated()

        .and().logout().permitAll()

        .and().formLogin()

        .and().csrf().disable();

        }

       

        @Autowired

        public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {

               auth.inMemoryAuthentication()

                       .withUser("copycoding").password(passwordEncoder().encode("copycopy")).roles("ADMIN");

               auth.inMemoryAuthentication()

                       .withUser("honggil").password(passwordEncoder().encode("hoho")).roles("USER");

        }

       

        @Bean

    public PasswordEncoder passwordEncoder() {

        return new BCryptPasswordEncoder();

    }

} 


Controller

 package com.copycoding.security;

 

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpSession;

 

import org.springframework.security.access.annotation.Secured;

import org.springframework.security.access.prepost.PreAuthorize;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.ResponseBody;

import org.springframework.web.bind.annotation.RestController;

 

@RestController

public class TestController {

       

        @RequestMapping("/")

    public @ResponseBody String home() throws Exception {

 

               return "Spring Boot ";

        }

       

        @RequestMapping("/page1")

    public @ResponseBody String pageNo1(HttpSession session, HttpServletRequest request) throws Exception {

              

               System.out.println("pageNo1=========ROLE_ADMIN===========>" + request.isUserInRole("ROLE_ADMIN"));

               System.out.println("pageNo1=========ROLE_USER============>" + request.isUserInRole("ROLE_USER"));

 

               return "Spring Boot : Page No 1";

        }

       

        @RequestMapping("/page2")

    public @ResponseBody String pageNo2(HttpSession session, HttpServletRequest request) throws Exception {

              

               System.out.println("pageNo2=======ROLE_ADMIN=============>" + request.isUserInRole("ROLE_ADMIN"));

               System.out.println("pageNo1=======ROLE_USER==============>" + request.isUserInRole("ROLE_USER"));

              

               return "Spring Boot : Page No 2";

        }

       

        @PreAuthorize("hasRole('ROLE_ADMIN')")

        @RequestMapping("/preRole1")

    public @ResponseBody String preRole1() throws Exception {

              

               return "@PreAuthorize : get role ROLE_ADMIN";

        }

       

        @PreAuthorize("hasRole('ROLE_USER')")

        @RequestMapping("/preRole2")

    public @ResponseBody String preRole2() throws Exception {

              

               return "@PreAuthorize : get role ROLE_USER";

        }

       

        @Secured("ROLE_ADMIN")

        @RequestMapping("/secRole1")

    public @ResponseBody String secRole1() throws Exception {

              

               return "@Secured : get role ROLE_ADMIN";

        }

 

        @Secured("ROLE_USER")

        @RequestMapping("/secRole2")

    public @ResponseBody String secRole2() throws Exception {

              

               return "@Secured : get role ROLE_USER";

        }

}


- copy coding -


12

+ Recent posts