이번 시간에는 로그인 과정과 로그인 성공시 생성된 token 을 어떻게 저장하고 어떻게 활용하는지 보여드리겠습니다.
token 은 처음 요청시 PHP 에서 생성해서 Mysql 데이타베이스에 저장하고, 이후 요청시에는 DB 에 저장된 token 을 불러서 보내주면 됩니다.
우선 JWT Class 를 사용하는 테마의 functions.php 에 넣어줍니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 |
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 함수를 사용하면 됩니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
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 으로 테스트를 해 볼까요?
Angular.js 의 코드는 크게 두 가지로 볼 수 있습니다.
token 을 받아오고 , cookie 에 저장한 다음 header 를 바꾸는 작업입니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
( 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 값을 가지고, 친구에게 메세지를 보내는 작업들은 다음 시간에 보여드리겠습니다.