Postman 과 Wp REST API 2.0 으로 JWT 서버엔진 만들기 (2)

[vc_row][vc_column][vc_column_text]이번 시간에는 로그인 과정과 로그인 성공시 생성된 token 을 어떻게 저장하고 어떻게 활용하는지 보여드리겠습니다.

token 은 처음 요청시 PHP 에서 생성해서 Mysql 데이타베이스에 저장하고, 이후 요청시에는 DB 에 저장된 token 을 불러서 보내주면 됩니다.

우선 JWT Class 를 사용하는 테마의 functions.php 에 넣어줍니다.

class JWT
{
	/**
	 * Decodes a JWT string into a PHP object.
	 *
	 * @param string      $jwt    The JWT
	 * @param string|null $key    The secret key
	 * @param bool        $verify Don't skip verification process 
	 *
	 * @return object      The JWT's payload as a PHP object
	 * @throws UnexpectedValueException Provided JWT was invalid
	 * @throws DomainException          Algorithm was not provided
	 * 
	 * @uses jsonDecode
	 * @uses urlsafeB64Decode
	 */
	public static function decode($jwt, $key = null, $verify = true)
	{
		$tks = explode('.', $jwt);
		if (count($tks) != 3) {
			throw new UnexpectedValueException('Wrong number of segments');
		}
		list($headb64, $bodyb64, $cryptob64) = $tks;
		if (null === ($header = JWT::jsonDecode(JWT::urlsafeB64Decode($headb64)))) {
			throw new UnexpectedValueException('Invalid segment encoding');
		}
		if (null === $payload = JWT::jsonDecode(JWT::urlsafeB64Decode($bodyb64))) {
			throw new UnexpectedValueException('Invalid segment encoding');
		}
		$sig = JWT::urlsafeB64Decode($cryptob64);
		if ($verify) {
			if (empty($header->alg)) {
				throw new DomainException('Empty algorithm');
			}
			if ($sig != JWT::sign("$headb64.$bodyb64", $key, $header->alg)) {
				throw new UnexpectedValueException('Signature verification failed');
			}
		}
		return $payload;
	}
	/**
	 * Converts and signs a PHP object or array into a JWT string.
	 *
	 * @param object|array $payload PHP object or array
	 * @param string       $key     The secret key
	 * @param string       $algo    The signing algorithm. Supported
	 *                              algorithms are 'HS256', 'HS384' and 'HS512'
	 *
	 * @return string      A signed JWT
	 * @uses jsonEncode
	 * @uses urlsafeB64Encode
	 */
	public static function encode($payload, $key, $algo = 'HS256')
	{
		$header = array('typ' => 'JWT', 'alg' => $algo);
		$segments = array();
		$segments[] = JWT::urlsafeB64Encode(JWT::jsonEncode($header));
		$segments[] = JWT::urlsafeB64Encode(JWT::jsonEncode($payload));
		$signing_input = implode('.', $segments);
		$signature = JWT::sign($signing_input, $key, $algo);
		$segments[] = JWT::urlsafeB64Encode($signature);
		return implode('.', $segments);
	}
	/**
	 * Sign a string with a given key and algorithm.
	 *
	 * @param string $msg    The message to sign
	 * @param string $key    The secret key
	 * @param string $method The signing algorithm. Supported
	 *                       algorithms are 'HS256', 'HS384' and 'HS512'
	 *
	 * @return string          An encrypted message
	 * @throws DomainException Unsupported algorithm was specified
	 */
	public static function sign($msg, $key, $method = 'HS256')
	{
		$methods = array(
			'HS256' => 'sha256',
			'HS384' => 'sha384',
			'HS512' => 'sha512',
		);
		if (empty($methods[$method])) {
			throw new DomainException('Algorithm not supported');
		}
		return hash_hmac($methods[$method], $msg, $key, true);
	}
	/**
	 * Decode a JSON string into a PHP object.
	 *
	 * @param string $input JSON string
	 *
	 * @return object          Object representation of JSON string
	 * @throws DomainException Provided string was invalid JSON
	 */
	public static function jsonDecode($input)
	{
		$obj = json_decode($input);
		if (function_exists('json_last_error') && $errno = json_last_error()) {
			JWT::_handleJsonError($errno);
		} else if ($obj === null && $input !== 'null') {
			throw new DomainException('Null result with non-null input');
		}
		return $obj;
	}
	/**
	 * Encode a PHP object into a JSON string.
	 *
	 * @param object|array $input A PHP object or array
	 *
	 * @return string          JSON representation of the PHP object or array
	 * @throws DomainException Provided object could not be encoded to valid JSON
	 */
	public static function jsonEncode($input)
	{
		$json = json_encode($input);
		if (function_exists('json_last_error') && $errno = json_last_error()) {
			JWT::_handleJsonError($errno);
		} else if ($json === 'null' && $input !== null) {
			throw new DomainException('Null result with non-null input');
		}
		return $json;
	}
	/**
	 * Decode a string with URL-safe Base64.
	 *
	 * @param string $input A Base64 encoded string
	 *
	 * @return string A decoded string
	 */
	public static function urlsafeB64Decode($input)
	{
		$remainder = strlen($input) % 4;
		if ($remainder) {
			$padlen = 4 - $remainder;
			$input .= str_repeat('=', $padlen);
		}
		return base64_decode(strtr($input, '-_', '+/'));
	}
	/**
	 * Encode a string with URL-safe Base64.
	 *
	 * @param string $input The string you want encoded
	 *
	 * @return string The base64 encode of what you passed in
	 */
	public static function urlsafeB64Encode($input)
	{
		return str_replace('=', '', strtr(base64_encode($input), '+/', '-_'));
	}
	/**
	 * Helper method to create a JSON error.
	 *
	 * @param int $errno An error number from json_last_error()
	 *
	 * @return void
	 */
	private static function _handleJsonError($errno)
	{
		$messages = array(
			JSON_ERROR_DEPTH => 'Maximum stack depth exceeded',
			JSON_ERROR_CTRL_CHAR => 'Unexpected control character found',
			JSON_ERROR_SYNTAX => 'Syntax error, malformed JSON'
		);
		throw new DomainException(
			isset($messages[$errno])
			? $messages[$errno]
			: 'Unknown JSON error: ' . $errno
		);
	}
}

토큰을 사용하기 위해서는 encode decode 함수를 사용하면 됩니다.

function login($request){

	global $wpdb;

	$creds = array();
	$creds['user_login'] = $request["username"];
	$creds['user_password'] =  $request["password"];
	$creds['remember'] = true;
	$user = wp_signon( $creds, false );

	if ( is_wp_error($user) ) {

		$ret = array();
		$ret['code'] = "auth_failed";
		$ret['data'] = array("status"=>403);
		$ret['message'] = "Invalid Credentials";
		return $ret;

	} else {

		$token['id'] = $user->ID;
		$token['pwd'] = $creds['user_password'];
		$web_token = JWT::encode($token, 'change.this');

		$client_info = array("ip"=>$_SERVER['REMOTE_ADDR'],"agent"=>$_SERVER['HTTP_USER_AGENT']);
		$str_client = json_encode($client_info);
		$sql = " INSERT INTO ".$wpdb->prefix."msgapi_token (user_id, token, client_id, expire_time ) VALUES (".$token['id'].", '".esc_sql($web_token)."', '".esc_sql($str_client)."',  date_add(current_timestamp, interval 1 day) );";
		$wpdb->query($sql);

		$ret = array();
		$ret['token'] = $web_token;
		$ret['display_name'] = $user->data->display_name;
		$ret['user_email'] = $user->data->user_email;
		$ret['user_nicename'] = $user->data->user_nicename;

		return $ret;

	}

}

$web_token = JWT::encode($token, ‘change.this’); 

encode 함수의 첫 번 째 값 id, pwd 를 담은 배열을 넣고, 두 번째 파라미터는 secret key 입니다. 서버 키로 어려운 키를 넣어주세요.

https://api.wordpress.org/secret-key/1.1/salt/

위 링크를 클릭해서 생성된 문자열을 사용하면 좋습니다.

그럼 POSTMan 으로 테스트를 해 볼까요?[/vc_column_text][/vc_column][/vc_row][vc_row][vc_column][vc_column_text]

username, password 를 받아서 token 을 DB 속에 넣고 그 값을 보여주게 됩니다.

DB 에는 어떤 값이 들어있는지 볼까요 ?

이렇게 받은 token 값은 쿠키나 localStorage 에 저장해서 사용할 수 있습니다.

이제 Android APP 에서 사용할 AngularJS 코드를 보기로 하죠.[/vc_column_text][/vc_column][/vc_row][vc_row][vc_column][vc_column_text]Angular.js 의 코드는 크게 두 가지로 볼 수 있습니다.

token 을 받아오고 , cookie 에 저장한 다음 header 를 바꾸는 작업입니다.

( function() {

  var app = angular.module( 'jwtAuth', [] );

  app.controller( 'MainController', function( $scope, $http ) {

    var apiHost = 'http://msgapi.wper.kr/wp-json';

    $http.post( apiHost + '/msgapi/v1/token', {
        username: 'admin',
        password: 'password'
      } )

      .then( function( response ) {
        console.log( response.data )
      } )

      .catch( function( error ) {
        console.error( 'Error', error.data[0] );
      } );

  } );

} )();

app.config( function( $httpProvider ) {
  $httpProvider.interceptors.push( [ '$q', '$location', '$cookies', function( $q, $location, $cookies ) {
    return {
      'request': function( config ) {
        config.headers = config.headers || {};
        //Assume that you store the token in a cookie.
        var globals = $cookies.getObject( 'globals' ) || {};
        //If the cookie has the CurrentUser and the token
        //add the Authorization header in each request
        if ( globals.currentUser && globals.currentUser.token ) {
          config.headers.Authorization = 'Bearer ' + globals.currentUser.token;
        }
        return config;
      }
    };
  } ] );
} );

중요한 포인트는 config.headers.Authorization = ‘Bearer ‘ + globals.currentUser.token;
이 부분입니다.

역시 POSTMan 에서 로그인 성공 이후 token 값을 가지고, 친구에게 메세지를 보내는 작업들은 다음 시간에 보여드리겠습니다.[/vc_column_text][/vc_column][/vc_row]