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 -


jquery validation 라이브러리를 사용하여 form에 입력하는 값에 대한 유효성을 검사할 수 있습니다사용방법도 매우 간단하여 rulesmessages만 설정해 주면 쉽게 적용이 가능 합니다


maven을 이용하지 않거나 추가적인 정보는 제작 사이트에 방문해서 자료를  찾을 수 있습니다.

https://jqueryvalidation.org/

 

pom.xml에 다음을 추가 합니다.

               <dependency>

                   <groupId>org.webjars</groupId>

                   <artifactId>jquery-validation</artifactId>

                   <version>1.19.0</version>

               </dependency>

 

추가한 라이브러리는 Maven > Update Project를 해서 반영해 줍니다.


jQuery Validation


 

jQuery Validation의 구조는 매우 간단 합니다.

 

$("form selector").validate({

    rules : { ... },

messages : { ... }

});

 

이렇게 rules, messages로 구성이 되어 있습니다.

 

rules를 이용하는 방법은 input 항목의 name 속성 값을 이용하여 설정을 진행하며

 

- 설정하려는 값이 하나이면

    name속성 값 : "required"

- 설정하려는 값이 두개 이상 이면

name속성 값 : {

            required : true,

            digits : true

    }

이렇게 {}로 묶어서 설정 합니다.

 

messages를 이용하는 방법도 input 항목의 name 속성 값을 이용하여 설정을 진행하며

- 설정하려는 값이 하나이면

    name속성 값 : : "필수 입력 항목 입니다."

- 설정하려는 값이 두개 이상 이면

name속성 값 : {

required : "필수 입력 항목 입니다.",

digits : "숫자만 입력하세요."

    }

이렇게 {}로 묶어서 설정 합니다.

이렇게 rules, messages가 쌍으로 구성되어 있습니다물론 messages를 작성하지 않아도 되는데 단점은 유효성 검사 결과를 영문으로 보여주기 때문에 영문으로 하지 않으려면 한글로 messages를 입력 해줘야 합니다.

 

적용 예)

$("form selector").validate({

    rules : {

        name속성 값 : "required",

        name속성 값 : {

                    required : true,

                    digits : true

        }

 

    },

    messages : {

        name속성 값 : "필수 입력 항목 입니다.",

        name속성 값 : {

            required : "필수 입력 항목 입니다.",

            digits : "숫자만 입력하세요."

        }

    }

});

 

이제 실제 적용을 하려는 화면 쪽 설정을 알아 봅니다.

먼저 maven에 추가한 라이브러리를 추가해 주고

<script src="/webjars/jquery/3.4.1/jquery.min.js"></script>

<script src="/webjars/jquery-validation/1.19.0/jquery.validate.min.js"></script>

 

form에 대한 유효성 설정을 진행 합니다.

 

<script type="text/javascript">

//<![CDATA[

        $(function() {

               $("#memberadd").validate({

                       rules : {

                              username : "required",

                              name : "required",

                              password : "required",

                              repassword : "required",

                              mobile1 : {required : true, digits:true},

                              mobile2 : {required : true, digits:true},

                              mobile3 : {required : true, digits:true},

                              tel1 : {digits:true},

                              tel2 : {digits:true},

                              tel3 : {digits:true},

                              email : {email:true}

                       },

                      

                       messages : {

                              username : "아이디는 필수 입력항목 입니다.",

                              name : "이름은 필수 입력항목 입니다.",

                              password : "비밀번호는 필수 입력항목 입니다.",

                              repassword : "비밀번호 확인은 필수 입력항목 입니다.",

                              mobile1 : "핸드폰은 필수 입력항목 입니다.",

                              mobile2 : "핸드폰은 필수 입력항목 입니다.",

                              mobile3 : "핸드폰은 필수 입력항목 입니다.",

                              email : "email 형식이 부정확 합니다."

                       }

              

               });

        });

 

//]]>

</script>

 

 

 

<body>

<form action="/user/membercreate" id="memberadd" method="POST">

    <table border="0">

         <tr>

             <td width="150" align="right">아이디 </td>

             <td><input type="text" id="username" name="username"  ></td>

         </tr>

         <tr>

             <td align="right">이름 </td>

             <td><input type="text" id="name" name="name" ></td>

         </tr>

         <tr>

             <td align="right">비밀번호 </td>

             <td><input type="password" id="password" name="password" ></td>

         </tr>

         <tr>

             <td align="right">비밀번호 확인 </td>

             <td><input type="password" id="repassword" name="repassword" ></td>

         </tr>

         <tr>

             <td align="right">소속회사 </td>

             <td><input type="text" class="form-control col-md-6" id="company" name="company" maxlength="30"></td>

         </tr>

         <tr>

             <td align="right">핸드폰 </td>

             <td>

                 <input type="text" class="form-control col-md-2" id="mobile1" name="mobile1" size="3">-

                 <input type="text" class="form-control col-md-2" id="mobile2" name="mobile2" size="4">-

                 <input type="text" class="form-control col-md-2" id="mobile3" name="mobile3" size="4">

             </td>

         </tr>

         <tr>

             <td align="right">email </td>

             <td><input type="text" id="email" name="email"></td>

         </tr>

         <tr>

             <td></td>

             <td align="right"><input type="submit" id="submit" value="등록"></td>

         </tr>

    </table>

</form>

 

</body>

 

Controller는 화면을 불러오도록 메소드를 하나 만들어 주고

        @RequestMapping("/membercreate")

        public String getMember( Model model){

              

              

               return "/user/MemberCreate";

        }

 

프로젝트를 실행을 해서 페이지를 불러옵니다.


jQuery Validation


이제 등록 버튼을 클릭해서 무슨 일이 벌어지는지 확인해 봅니다.


jQuery Validation


이렇게 입력 항목에 대한 검사를 해줍니다.

개인적인 생각은 그냥 사용 하기에는 쓸만 하겠지만 요청사항이 까다로운 사이트에서는 사용이 힘들지 않을까 생각 됩니다.


- copy coding -


1. substring

 

문자열중 일부를 뽑아오거나 여러 Data를 하나의 문자열로 받아 다시 각 항목별로 나누어 분류하는데 많이 사용합니다중간에 구분자가 없는 경우에 사용합니다.  문자열이 길어지면 눈이 튀어나와 힘들죠.


substring의 정의는 다음과 같습니다.

public String substring(int beginIndex, int endIndex)

  beginIndex : 시작 index

  endIndex : 종료 index

 

사용시 주의 사항은 beginIndex0에서 시작하고 endIndex는 자르려는 글자 끝 index보다 1을 더해줘야 합니다.

 String str = “ABCDEFGHIJKL”;

문자열

A

B

C

D

E

F

G

H

I

J

K

L

Index

0

1

2

3

4

5

6

7

8

9

10

11

 

A부터 C까지만 잘라 오려면

String getStr1 = str.substring(0, 3);


D부터 K까지만 잘라 오려면

String getStr2 = str.substring(3, 11);

 

F부터 끝까지 가져오려면

String getStr3 = str.substring(5);


간단한 테스트 입니다.

        public static void subString() {

              

               String str = "ABCDEFGHIJKL";

              

               String getStr1 = str.substring(0, 3);

               String getStr2 = str.substring(3, 11);

               String getStr3 = str.substring(5);

              

               System.out.println("str.substring(0, 3) : "+ getStr1);

               System.out.println("str.substring(3, 11) : "+ getStr2);

               System.out.println("str.substring(5) : "+ getStr3);

              

        }


java substring split



2. split

 

substring과 같이 여러 Data를 구분자를 추가하여 하나의 문자열로 묶어서 수신한 후 다시 나누거나 문자열에 규칙적인 반복 문자가 있을 경우 이용하여 문자열을 나누어 배열화 하는 기능입니다중간에 꼭 규칙적인 문자가 있어야 가능하겠죠.

 

split의 정의는 다음과 같습니다.

public String[] split(String regex)

regex : 규칙적 어구

  

기호를 구분자로

 

String str1 = "A-B-CDEFG-HIJKL";

str1 문자열에는 “-“ 이 문자가 반복되어 구분자로 사용할 수 있습니다.

String getStr1[] = str1.split("-");

이렇게 하면 getStr1[] 배열에 문자열이 “-“으로 나뉘어져 들어가게 됩니다. 주민번호, 전화번호등에 가장 많이 사용하는 방법이죠.

 

String str1 = "A-B-CDEFG-HIJKL";

 

String getStr1[] = str1.split("-");

 

for(int i=0; i<getStr1.length;i++) {

        System.out.println("getStr1 ["+ i + "] : " + getStr1[i]);

}


java substring split



문자를 구분자로

 

String str2 = "ABTTCDETTFGHIJKTTL";

str2 문자열에는 “TT” 문자가 반복적으로 들어있어 구분자로 사용할 수 있습니다.

 

String getStr2[] = str2.split("TT");

이렇게 하면 “TT”로 구분된 문자가 getStr2[] 배열에 들어갑니다

 

String str2 = "ABTTCDETTFGHIJKTTL";

 

String getStr2[] = str2.split("TT");

 

for(int i=0; i<getStr2.length;i++) {

        System.out.println("getStr2 ["+ i + "] : " + getStr2[i]);

}


java substring split



특수문자를 구분자로

 

String str2 = "A|B|CDEFG|HIJKL";

str2는 특수문자 “|”가 중간에 구분자로 들어가 있습니다. 특수문자를 일반적인 문자처럼 구분자로 사용하게 되면 엉뚱한 결과가 나옵니다. 엉뚱하다기 보다는 그냥 글자를 하나씩 배열에 입력하고 끝나게 됩니다. 이런 경우는 두가지 방법으로 처리를 하는데 하나는 특수문자 앞에 “\\”를 써주거나 특수문자를 “[]”로 감싸줍니다.

 

        String str3 = "A|B|CDEFG|HIJKL";

              

//      String getStr3[] = str3.split("[|]");

// String getStr3[] = str3.split("[.]");

        String getStr3[] = str3.split("\\|");

       

        for(int i=0; i<getStr3.length;i++) {

               System.out.println("getStr3 ["+ i + "] : " + getStr3[i]);

        }


java substring split


- 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 -


1234567

+ Recent posts