前情提要
Line Notify API 串接实作-前置作业
好的,根据前一篇所提到的OAuth2.0流程,我们需要
1.取得code(Authorization Grant)
2.取得access token
才能利用后续的服务
取得code
GET https://notify-bot.line.me/oauth/authorize
需要参数
成功则回传至请求所带的redirect_uri
失败则回传至请求所带的redirect_uri
以下程式码
Controller送出请求
@GetMapping("sendToOauth")public void sendToOauth(@RequestParam(name="account")String account,HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {String url = userService.getOAuthCode(account);response.sendRedirect(url);}
getOAuthCode取得UR字串
@Overridepublic String getOAuthCode(String account) {StringBuilder sb = new StringBuilder();//资料库取得使用者资料User findResult = Optional.of(userDao.findFirstByAccount(account)).orElseThrow(()->new UserServiceException(UserServiceErrorEnum.USER_DATA_NOT_FOUND.getError(),UserServiceErrorEnum.USER_DATA_NOT_FOUND.getMessage()));//设定请求资料LineOAuthRequest lineOAuthRequest = new LineOAuthRequest();lineOAuthRequest.setClient_id(client_id);lineOAuthRequest.setRedirect_uri(redirect_uri);lineOAuthRequest.setState(findResult.getState());Map<String,Object> variableParams = lineOAuthRequest.getUriParams();//组装GetOAuth URL字串sb.append(LineNotifyUrl.GET_OAUTH.getUrl()).append("?");for(String key:variableParams.keySet()) { sb.append(key) .append("=") .append(String.valueOf(variableParams.get(key))) .append("&");}sb.deleteCharAt(sb.length()-1);return sb.toString();}
LineOAuthRequest物件
@Data@NoArgsConstructorpublic class LineOAuthRequest {private String client_id;private String redirect_uri;private String response_type = "code";private String scope = "notify";private String state;private String response_mode="form_post";public Map<String, Object> getUriParams(){try { //检查值并回传 属性名与值构成的键值对beforeSendCheck();return ApiUtil.getRequestUriVariables(this);}catch (Exception e) {throw new LineOauthRequestException(4,e.getMessage());}}public void beforeSendCheck() {if(!StringUtils.hasText(client_id)) {throw new LineOauthRequestException(1,"client_id is empty");}else if(!StringUtils.hasText(redirect_uri)) {throw new LineOauthRequestException(2,"redirect_uri is empty");}else if(!StringUtils.hasText(state)) {throw new LineOauthRequestException(3,"state is empty");}}}
ApiUtil
public static <T> Map<String, Object> getRequestUriVariables(T source) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{Map<String, Object> returnMap = new HashMap<>();Field[] fields = source.getClass().getDeclaredFields();for(int i=0;i<fields.length;i++) {String fieldName = fields[i].getName();String firstLetter = fieldName.substring(0,1).toUpperCase();String getter = "get"+firstLetter+fieldName.substring(1);Method method = source.getClass().getMethod(getter, new Class[] {});Object value = method.invoke(source, new Object[] {}); returnMap.put(fieldName, value);}return returnMap;}
取得access token
POST https://notify-bot.line.me/oauth/token
ContentType: application/x-www-form-urlencoded
需要参数
code无论最后结果成功或只能使用一次,并设定有失效时限
这边有遇到奇怪的点,在取得access token的请求设定redirect_uri,如果与取得code设定的redirect_uri不一致的话,会回传redirect_uri not match的错误。
就算我在Callback URL设定第二个redirect_uri依然是一样的错误,如果有大大知道原因欢迎下方留言告诉我。
成功回应
Content-Type:application/json
以下程式码
getToken方法
@Overridepublic void getToken(LineOAuthResponse lineOAuthResponse) throws InvalidKeyException, UserServiceException, IllegalBlockSizeException, BadPaddingException {//LineOAuthResponse:getOAuth成功回传物件//设定表头参数String code = lineOAuthResponse.getCode();String state = lineOAuthResponse.getState();HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); MultiValueMap<String, String> params= new LinkedMultiValueMap<String, String>(); params.add("grant_type", "authorization_code"); params.add("code", code); params.add("redirect_uri", token_redirectUri); params.add("client_id", client_id); params.add("client_secret", token_secret);HttpEntity<MultiValueMap<String, String>> entity = new HttpEntity<>(params,headers);LineTokenResponse result = restTemplate.postForObject(LineNotifyUrl.GET_TOKEN.getUrl(), entity, LineTokenResponse.class);if(HttpStatus.OK.value()!=result.getStatus()) {throw new UserServiceException(UserServiceErrorEnum.USER_TOKEN_BACK_ERROR.getError(),UserServiceErrorEnum.USER_TOKEN_BACK_ERROR.getMessage());}//成功取得回应后根据state取出对应的用户资料User origin = Optional.of(userDao.findFirstByPassword(state)).orElseThrow(()->new UserServiceException(UserServiceErrorEnum.USER_DATA_NOT_FOUND.getError(),UserServiceErrorEnum.USER_DATA_NOT_FOUND.getMessage()));//新增token值后更新回资料库origin.setToken(result.getAccess_token());userDao.save(origin);}
LineTokenResponse物件
@Datapublic class LineTokenResponse {private Integer status;private String message;private String access_token;}
OK 取得access token,我们在就可以传讯息给使用者啦~
下篇
Line Notify API 串接实作(二)-推播讯息
参考资料
Line Notify Document