본문으로 건너뛰기

Android scheme 로 앱 실행

무료2017-01-15#Android#Android自定义scheme#Android呼起客户端#网页打开App#Android拦截请求

페이지에서 Android 클라이언트를 실행하는 방법

一.커스텀 Scheme

Android 앱/컴포넌트 간 통신 방식 중 하나는 intent 입니다. 앱은 intent filter 를 등록하여 어떤 intent 에 관심이 있는지 선언할 수 있습니다. 다른 앱이 intent 를 전송하면 시스템 레벨 브로드캐스트를 통해 전달되며, 미리 등록한 intent filter 와 일치하면 앱은 해당 intent 를 받습니다 (앱이 실행 중이지 않더라도"웨이크업"되며, 소위 암시적 Activity启动가 수행됨). 그런 다음 intent 에 포함된 데이터를 가져와 추가 처리를 수행합니다.

즉, 시스템 브로드캐스트를 통해 한 번 실행할 기회를 얻는 것입니다. 예를 들어 manifest 에 정적으로 intent filter 를 등록하여 커스텀 scheme 를 선언합니다:

<activity android:name=".MainActivity">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />

        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>

    <!--scheme 등록-->
    <intent-filter>
        <action android:name="android.intent.action.VIEW"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <!--BROWSABLE 는 이 Activity 가 브라우저에서 안전하게 호출될 수 있음을 지정-->
        <category android:name="android.intent.category.BROWSABLE"/>
        <!--커스텀 scheme 선언, http, https 와 유사-->
        <data android:scheme="hoho"/>
    </intent-filter>
</activity>

action, category, data 는 모두 완전히 일치해야 합니다. 여기서는 2 개의 category 가 선언되었으며, intent 가 이 2 개의 category 를 동시에 포함할 때만 일치로 간주됩니다. android.intent.category.DEFAULT 는 기본값이며, 실제 의미가 있는 것은 android.intent.category.BROWSABLE 로, 이 activity 를 브라우저에서 시작할 수 있음 (앱을 호출할 수 있음) 을 나타냅니다. 이후의 data 는 트리거 조건을 한정하며, schemehoho 일 때만 일치합니다. 예를 들어 브라우저가 hoho://abc 에 액세스하면 일치에 성공하고 앱이 실행됩니다.

二.데이터 가져오기

onCreate 에서 intent 를 가져와 uri 를 추출합니다:

@Override
protected void onCreate(Bundle savedInstanceState) {
    //...

    // uri 파라미터 가져오기
    Intent intent = getIntent();
    String scheme = intent.getScheme();
    Uri uri = intent.getData();
    String str = "";
    if (uri != null) {
        String host = uri.getHost();
        String dataString = intent.getDataString();
        String from = uri.getQueryParameter("from");
        String path = uri.getPath();
        String encodedPath = uri.getEncodedPath();
        String queryString = uri.getQuery();

        //...uri 에 따라 어느 페이지를 열지, 또는 어느 기능을 열지 판단
    }
}

여기의 URI 는 표준 URI 로, 프로토콜, 호스트명, 포트 번호, 경로, 쿼리 문자열 등이 있지만, 일반적으로 커스텀 scheme 는 이렇게 복잡하게 만들 필요가 없으며, path/query 로 간단한 구분만 하면 됩니다. 예를 들어:

// path 로 구분
hoho://toFeature/login
// query 로 구분
hoho://open?feature=login

물론 포트 번호 등으로 구분할 수도 있지만 큰 차이는 없습니다.

三.온라인 페이지에서 앱 실행

브라우저가 커스텀 scheme 요청을 보내면 시스템 브로드캐스트가 이를 받아 각 앱에 배포합니다. 페이지가 요청을 보내는 방식은 다양합니다:

location.href
iframe.src
a.href
img.src
...기타 요청을 보낼 수 있는 방식

이러한 방식에는 강약 차이가 있습니다. 예를 들어 location.href 는 강한 반면, img.src 는 매우 약합니다. 적어도 브라우저가 해당 요청을 시스템 브로드캐스트에 넘기기로 결정할 정도의 강도는 되어야 합니다. 예를 들어 img 가 커스텀 scheme 를 요청해도 브라우저는 시스템 브로드캐스트에 넘길 필요가 없다고 판단합니다. 일반적으로 가장 강한 2 가지 방식만 사용합니다: location.hrefiframe.src 입니다. iframe 을 숨겨 커스텀 scheme 를 몰래 요청하는 방식이 더 많이 사용됩니다. 알려지지 않은 부작용이 없기 때문입니다 (location 방식은 아마도 기록 스택에 기록되거나 현재 페이지가 unload 될 수 있지만, iframe그렇게 심각한 부작용이 절대 없습니다).

하지만 어느 방식으로도 앱이 실행되었는지 알 수 없습니다. 앱이 설치되지 않았을 수도 있고, intent 가 일치하지 않았을 수도 있지만, 페이지에는 알 방법이 없습니다. 따라서 일반적으로 앱을 실행하는 페이지는 다운로드 페이지로 자동跳转를 지연시킵니다. 앱 실행 성공 여부와 상관없이, 이는 어쩔 수 없는 조치입니다.

페이지가 요청을 보내는 것 외에도 더 강한 방식이 있습니다: 앱에서 요청을 보내는 방식입니다. 예를 들어:

// webview 에서 요청 보내기
webview.loadUrl(mySchemeUri);

이 시작점은 앱 레벨이며, WebView 내 페이지 요청보다 더 강합니다. 따라서 일반적으로 Hybrid App 에서는 클라이언트가 이러한 인터페이스를 제공하여 서드파티로跳转하는 데 사용합니다. 페이지 요청보다 더 강합니다.

四.Intent Scheme URL 공격

커스텀 Scheme 에는 보안 위험이 존재합니다. 예를 들어:

  • 더 우선순위가 높은 동일한 intent filter 를 등록하여 scheme uri 를 탈취

  • 跳转할 커스텀 scheme 형식을 알면 피싱 페이지로跳转 가능 (확실히 앱 내에서 열린 페이지이지만, 이는 제 3 자가 만든 가짜)

  • ...기타 위험

일반적으로 커스텀 scheme 는 공개되지 않지만, 유출될 수 있습니다 (앱 디컴파일 등의 방식으로). scheme 인터페이스 자체에 적절한 방비가 필요합니다. intent 를 수신할 때는 다음과 같이 할 수 있습니다:

    // BROWSABLE 카테고리 없이 Activity 启动 금지
    intent.addCategory("android.intent.category.BROWSABLE");
    // 명시적 호출 금지
    intent.setComponent(null);
    // 셀렉터 intent 가 포함된 intent 금지
    intent.setSelector(null);

커스텀 scheme 에서 온 모든 입력을 신뢰하지 않으며,跳转 인터페이스에는 화이트리스트 제한도 필요합니다.

五.WebView Scheme 화이트리��트

WebView 는 페이지 컨테이너로서 페이지 요청을 필터/인터셉트할 수 있습니다:

class MyWebClient extends WebViewClient {

    @Override
    public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
        if (url.startsWith("hoho://")) {
            return null;
        }

        return super.shouldInterceptRequest(view, url);
    }

    @Override
    public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
        String scheme = request.getUrl().getScheme();
        if (scheme.equals("hoho")) {
            return null;
        }

        return super.shouldInterceptRequest(view, request);
    }
}

위 코드는 API[11-20] 용이며, 21 부터는 String url 이 deprecated 되고 WebResourceRequest request 가 추가되었습니다. API21+ 에서는 WebResourceRequest request 형식만 트리거되므로, 호환성을 위해 둘 다 다시 작성해야 합니다.

필터 조건을 만족하는 것은 인터셉트되므로, WeChat 내에서는 앱을 실행할 수 없습니다. 화이트리스트에 없어 인터셉트되고 시스템 브로드캐스트에 전달되지 않기 때문입니다.

인터셉트된 경우 iframe 방식의 장점이 나타납니다. a.hreflocation.href 는 모두 페이지跳转를 일으켜 "웹페이지를 열 수 없습니다...net::ERR_UNKNOWN_URL_SCHEME 때문"이라고 표시되지만, iframe 방식은 현재 페이지에 영향을 주지 않습니다.

六.Demo

apk 다운로드 주소:http://ayqy.net/apk/android-scheme.apk

테스트 페이지:http://ayqy.net/temp/android-scheme.html

마지막으로

Android Studio 는 정말 너무 느립니다. eclipse 가 그리웁니다. 또한, @旭 님께 감사드립니다.

참고 자료

댓글

아직 댓글이 없습니다

댓글 작성