728x90

"자바로 배우는 자료구조" 동영상 강의를 듣고 있다.

"자료구조와 함께 배우는 알고리즘 입문" 자바편 책과는 무관하다.

처음에는 이 책의 동영상 강의인줄 알고 찾아서 듣기 시작했는데, 강의를 들으면서 내가 몰랐던 부분, 놓치고 있던 부분 등을 알 수 있어 많은 도움이 된다.


다형성이 자바의 핵심이라고 하면서 C++과 가장 다른 점이라고 강조를 한다.

동영상 강의를 들을 때는 알 거 같은데 막상 뒤돌아서면 머리속에 남는게 없다. 아직 내 것으로 만들지 못해서다.

자바의 정석 책을 꺼내서 다형성 부분을 살펴보니까 강의에서 했던 내용이랑 거의 비슷하게 설명이 잘 되어 있다.


- 수퍼클래스 타입의 참조변수가 서브클래스 타입의 객체를 참조할 수 있다.

   Computer test = new Notebook();



출처: https://link2me.tistory.com/1269?category=942905 [소소한 일상 및 업무TIP 다루기]

- 수퍼클래스 타입의 참조변수가 서브클래스 타입의 객체를 참조할 수 있다.

   Computer test = new Notebook();



출처: https://link2me.tistory.com/1269?category=942905 [소소한 일상 및 업무TIP 다루기]

다형성(Polymorphism)

- 수퍼클래스 타입의 참조변수가 서브클래스 타입의 객체를 참조할 수 있다.

   Computer test = new Notebook();



출처: https://link2me.tistory.com/1269?category=942905 [소소한 일상 및 업무TIP 다루기]

다형성(Polymorphism)

- 수퍼클래스 타입의 참조변수가 서브클래스 타입의 객체를 참조할 수 있다.

   Computer test = new Notebook();



출처: https://link2me.tistory.com/1269?category=942905 [소소한 일상 및 업무TIP 다루기]
지금까지 우리는 생성된 인스턴스를 다루기 위해서, 인스턴스의 타입과 일치하는 타입의 참조변수만을 사용했다.

Notebook notebook = new Notebook();

Computer computer = new Computer();



수퍼 클래스 타입의 참조변수로 서브 클래스 타입의 객체를 참조할 수 있다. (동영상 강의)

조상 클래스 타입의 참조변수로 자손 클래스의 인스턴스를 참조할 수 있도록 하였다. (자바의 정석)


클래스가 상속관계에 있는 경우 조상 클래스 타입의 참조변수로 자손 클래스의 인스턴스를 참조하는 것도 가능하다.

Computer test = new Notebook();


반대로 자손 타입의 참조변수로 조상 타입의 인스턴스를 참조하는 것은 불가능하다.

참조변수가 사용할 수 있는 멤버의 개수는 인스턴스의 멤버 개수보다 같거나 적어야 한다.


아래 예제는 생활코딩 강좌와 자바의 자료구조 강좌를 혼용하여 작성했다.

생활코딩 강좌가 개념을 좀 더 명확하게 해주는 거 같기는 하다. 자바 자료구조 강좌가 실제 코딩에 좀 더 접근한 설명으로 도움이 되는 부분도 있고 둘 다 들어보면 좋을 거 같다.


package section2;

class A {
    public String x() {    return "A.x"; }
}

class B extends A {
    String screensize ="1920";
    public String x() {
        String result = super.x() + "/" + screensize;
        return result;
    }

    public String y() { return "y"; }
}

class B2 extends A {
    public String x() { return "B2.x"; }
}

interface I1 {
    public String A();
}

interface I2 {
    public String B();
}

class C implements I1, I2 {

    @Override
    public String B() { return "B"; }

    @Override
    public String A() { return "A";    }
}

public class PolymorphismDemo1 {

    public static void main(String[] args) {
        A obj = new B();
        // 수퍼 클래스(조상) 타입의 참조변수로 서브 클래스(자손) 타입의 객체를 참조할 수 있다.
        // 반대로 자손 타입의 참조변수로 조상 타입의 인스턴스를 참조하는 것은 불가능하다.
        System.out.println(obj.x()); // 출력 : A.x/1920, dynamic binding
        // 클래스 B를 클래스 A의 데이터 타입으로 인스턴스화 했을 때,

        // 클래스 A에 존재하는 맴버만이 클래스 B의 맴버가 된다.
        // 동시에 클래스 B에서 오버라이딩한 맴버의 동작방식은 그대로 유지한다.
        //obj.y(); // 존재하지 않는 메소드처럼 실행되지 않는다.
       
        A obj2 = new B2();
        System.out.println(obj2.x()); // 출력 : B2.x, 두 인스턴스의 메소드 x를 호출한 결과는 서로 다르다.
       
        C obj3 = new C();
        I1 objI1 = new C();
        I2 objI2 = new C();

        System.out.println(obj3.A()); // 출력 : A
        System.out.println(obj3.B()); // 출력 : B
        System.out.println(objI1.A()); // 출력 : A
        System.out.println(objI2.B()); // 출력 : B
    }

}





'안드로이드 > Java 문법' 카테고리의 다른 글

Java 제네릭(Generic)  (0) 2019.08.27
Java 추상 클래스  (0) 2019.08.19
How to validate IP address with regular expression  (0) 2019.06.17
자바 클래스 개념 이해 예제  (0) 2019.05.27
자바 Arraylist  (0) 2019.05.26
블로그 이미지

Link2Me

,
728x90
안드로이드 팝업창에서 입력 정보를 받아서 처리하는 AlertDialog 코드 예시다.
IP 등록 함수 구현 내용은 적어두지 않는다.

AlertDialog.Builder alert_ipedit = new AlertDialog.Builder(MainActivity.this);
alert_ipedit.setTitle("XXX 스위치 IP주소 등록");
alert_ipedit.setMessage("(예시) 10.10.10.10/24");
final EditText etip = new EditText(MainActivity.this);
alert_ipedit.setView(etip);
if(ipsubnet != null){
    etip.setText(ipsubnet.trim()); // 한번 입력한 값이 있으면 표시한다.
}
// 확인 버튼 설정
alert_ipedit.setPositiveButton("등록", new DialogInterface.OnClickListener() {
    @Override
    public void onClick(DialogInterface dialog, int which) {
        ipsubnet = etip.getText().toString().trim();
        if(ipsubnet.length() == 0) {
            AlertDialog.Builder subnetmask_confirm = new AlertDialog.Builder(MainActivity.this);
            subnetmask_confirm.setMessage("입력된 정보가 없습니다.").setCancelable(false).setPositiveButton("확인",
                    new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) { // 'YES'
                            dialog.dismiss();
                        }
                    });
            AlertDialog alert1 = subnetmask_confirm.create();
            alert1.show();
            return;
        }
        String result = IPSubnetMask(ipsubnet);
        if(result.equals("1")){
            IPReg(ipsubnet); // IP 등록함수 실행
        } else if(result.equals("2")){
            AlertDialog.Builder subnetmask_confirm = new AlertDialog.Builder(MainActivity.this);
            subnetmask_confirm.setMessage("ip 주소 입력이 잘못되었습니다.\n입력값 :"+ipsubnet).setCancelable(false).setPositiveButton("확인",
                    new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) { // 'YES'
                            dialog.dismiss();
                        }
                    });
            AlertDialog alert1 = subnetmask_confirm.create();
            alert1.show();
        } else if(result.equals("3")){
            // 서브넷 마스크 맞는지 팝업창
            AlertDialog.Builder subnetmask_confirm = new AlertDialog.Builder(MainActivity.this);
            subnetmask_confirm.setMessage("서브넷 마스크가 맞습니까?\n입력값 :"+ipsubnet).setCancelable(false).setPositiveButton("확인",
                    new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) { // 'YES'
                            IPReg(ipsubnet); // IP 등록함수 실행
                        }
                    }).setNegativeButton("취소",
                    new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            dialog.dismiss();
                        }
                    });
            AlertDialog alert1 = subnetmask_confirm.create();
            alert1.show();
        } else {
            AlertDialog.Builder subnetmask_confirm = new AlertDialog.Builder(MainActivity.this);
            subnetmask_confirm.setMessage("IP 주소 입력 정보가 잘못되었습니다.\n입력값 :"+ipsubnet).setCancelable(false).setPositiveButton("확인",
                    new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) { // 'YES'
                            dialog.dismiss();
                        }
                    });
            AlertDialog alert1 = subnetmask_confirm.create();
            alert1.show();
        }
    }
});
alert_ipedit.setNegativeButton("취소", new DialogInterface.OnClickListener() {
    @Override
    public void onClick(DialogInterface dialog, int which) {
        dialog.dismiss();
    }
});
alert_ipedit.show();




블로그 이미지

Link2Me

,
728x90

File Upload 공격

설명
악의적인 스크립트 파일을 웹 서버에 업로드하여 접근할 경우 웹 서버 사용자 권한으로 실행이 되는 취약점이다.
 발생원인 업로드할 파일이 안전한지 검사하지 않아 발생한다.

파일이 업로드되는 페이지(게시판, SNS 등)에 악성파일(웹셀)을 업로드가 가능한 경우에 발생한다.
 위험성 환경마다 권한이 다를 수 있지만, 서버를 직접 컨트롤할 수 있어 서비스에 치명적인 영향을 줄 수 있다.
 대응 ㅇ업로드되는 파일의 크기, 개수, 종류를 제한한다.
ㅇ업로드한 파일을 외부에서 접근할 수 없는 경로에 저장한다.
ㅇ업로드한 파일의 저장 경로와 파일명을 외부에서 알 수 없도록 한다.

ㅇ실제 저장되는 파일명은 난수를 이용해 유추 불가능하도록 생성하여

   외부로부터 직접적인 접근 불가능하게 구현한다.

ㅇ업로드한 파일에 대한 웹서버의 실행권한을 제거하고 저장한다.

 

 

PHP File Upload 공격이 가능하려면?
1) php 파일을 서버에 업로드할 수 있어야 한다.
2) 웹 브라우저를 통해서 접근이 가능해야 한다.

 

이를 방지하려면

. 업로드되는 파일의 확장자를 검사하여 허락된 타입만 저장한다.

. File Upload 디렉토리를 Web root 디렉토리를 벗어난 위치에 설정한다.

. 해커가 파일을 찾을 수 없도록 업로드 파일의 이름과 확장자를 난수화하여 저장한다.

. 저장되는 파일은 실행권한이 없도록 권한 변경하여 저장한다.

. 실제 저장되는 파일명과 View로 보여주는 파일명을 다르게 한다.

  DB에 보여줄 파일명과 실제 저장된 파일명을 모두 저장하고, View에는 업로드시의 파일명으로 보여주고

  다운로드를 누르면 실제 파일명을 View 파일명으로 변경하여 저장되도록 구현한다.

. 특수문자가 포함된 경우 업로드를 막는다.

 

'Web 프로그램 > Web Security' 카테고리의 다른 글

HTML Purifier 사용법 예시  (0) 2023.02.28
remote IP address  (0) 2021.03.24
파일 다운로드 공격 방지  (0) 2019.07.05
Cross-Site Scripting (XSS) 방지  (0) 2019.06.20
SQL Injection 공격 방지  (1) 2019.06.19
블로그 이미지

Link2Me

,
728x90

Cross-Site Scripting (XSS)

설명

공격자가 웹 페이지에 악성 스크립트를 삽입할 수 있는 취약점이다.

사용자가 입력한 정보를 출력할 때 스크립트가 실행되도록 하는 공격기법이다.

다른 사이트로 어떤 정보를 전송하는 행위가 주로 일어나기 때문에 사이트간 스크립팅이라는 이름을 가지고 있다.

 발생원인

웹 페이지에서 사용자로부터 입력받은 값을 제대로 검사하지 않고 사용할 경우 발생한다.

 위험성

ㅇ사용자의 쿠키 정보 노출로 세션 하이재킹과 같은 공격이 실행될 수 있다.

ㅇ사용자를 피싱 사이트로 접속하게 만들어 사용자의 중요 정보를 탈취할 수 있다.

ㅇ클라이언트(브라우저)에서 악성코드가 실행되어 사용자 PC를 좀비화할 수 있다.

 대응

입력값에 대해 정규식을 이용하여 정확하게 허용되는 패턴의 데이터만 입력되도록 한다.
ㅇ서버로 들어오는 모든 요청에 대해 XSS 필터를 적용하여 안전한 값만 전달되어 사용되도록 한다.
ㅇ출력값에 대해 HTML 인코딩을 적용하여 스크립트가 동작되지 않도록 한다.


공격자가 의도적으로 브라우저에서 실행될 수 있는 악성 스크립트를 웹 서버에 입력 또는 이것을 출력 시 위험한 문자를 중성화시키지 않고 처리하는 애플리케이션의 개발 과정에서 발생한다.
XSS는 일반적으로 자바스크립트에서 발생하지만, VB 스크립트, ActiveX 등 클라이언트에서 실행되는 동적 데이터를 생성하는 모든 언어에서 발생이 가능하다.

가장 일반적인 방법은 게시판 같은 곳에 HTML 문서에 <script>를 이용하여 이 스크립트 태그 안에 악성 스크립트를 저장하는 방식이다.
즉 텍스트만 표시되도록 설계된 어떤 게시판에 <script> “악성 스크립트” </script>과 같은 태그를 포함한다.

웹해킹의 기본은 웹페이지 소스보기부터 출발한다.
애초에 설계때부터 추후에 등장할 지 모를 버그 등 위험성을 모두 고려하여 설계하는것이 시큐어코딩이다.
XSS 취약점을 근본적으로 제거하기 위해서는 스크립트 등 해킹에 사용될 수 있는 코딩에 사용되는 입력 및 출력 값에 대해서 검증하고 무효화시켜야 한다.
입력 값에 대한 유효성 검사는 데이터가 입력되기 전에 가능하면, 입력 데이터에 대한 길이, 문자, 형식 및 사업적 규칙 유효성을 검사해야 한다.
출력 값을 무효화하기 위해서는 XSS 공격은 기본적으로 <script> 태그를 사용하기 때문에 XSS 공격을 차단하기 위해 태그 문자(<, >) 등 위험한 문자 입력 시 문자 참조(HTML entity)로 필터링하고, 서버에서 브라우저로 전송 시 문자를 인코딩하는 것이다.
예를 들어, 문자 “<”는 동일한 의미의 HTML “&lt;”로 변경한다. HTML 엔터티는 대부분의 브라우저에서 특수한 의미를 가지지 않으며, 단순한 문자로 처리된다.
이렇게 인코딩하면 사용자는 <script>가 <script>로 보이지만 HTML 문서에서는 &lt;script&gt;로 나타나서 브라우저에서 일반 문자로 인식하고 스크립트로 해석되어 실행되지는 않는다.

htmlspecialchars($str,ENT_QUOTES,'UTF-8');
이 함수는 문자열에서 특정한 특수 문자를 HTML 엔티티로 변환한다.
& (앰퍼샌드) 는 &amp; 로 바꾼다.
" (큰따옴표) 는 &quot; 로 바꾼다.
' (작은따옴표) 는 &#039; 로 바꾼다.
< (~ 보다 작다는 기호) 는 &lt; 로 바꾼다.
> (~ 보다 크다는 기호) 는 &gt; 로 바꾼다.
htmlspecialchars 함수를 사용하면 악성 사용자로부터 XSS 공격을 방지 할 수 있다.

게시판의 게시글, 댓글, 쪽지 등에 자바스크립트 코드가 들어가지 않도록 필터링해야 한다.
HTTP  헤더에 문자 인코딩을 지정하지 않는 페이지는 XSS 공격의 위험성 있으므로 반드시 문자 인코딩을 설정해야 한다. 모든 파라미터는 변조할 수 있다는 전제하에 Web 애플리케이션을 작성해야 한다.

아래 코드는 htmlspecialchars 함수, strip_tags 함수를 이용하여 사용할 경우와 사용하지 않을 경우의 XSS 공격이라는 가정하의 예제인 셈이다.

<?php
error_reporting(E_ALL);
ini_set("display_errors", 1);
ini_set('default_charset','UTF-8');

$str = "<h2> 'PHP' morden programing.</h2>";
// HTML 태그 속성 반영한 출력
echo $str.'<br/>';

$str2 = htmlspecialchars($str,ENT_QUOTES,'UTF-8'); // HTML 태그에 사용되는 특수문자를 HTML 엔티티로 변환
// HTML 태그 속성 제거된 문자열 출력
echo $str2.'<br/>';

// 정규식으로 HTML 태그 제거
$str3 = preg_replace("/(<([^>]+)>)/i","",$str);
echo $str3.'<br/>';
// ? : 0 또는 하나     {0,1} 와 같다.
// + : 1개 이상       {1,} 와 같다.
// * : 0 개 이상      {0,} 과 같다.
// [] : []안에 있는 문자열 중 한문자
// {} : {} 바로 앞에 있는 문자열(또는 문자)의 개수
// () : ()안에 있는 문자들의 그룹화
// | : OR 연산자 기능

// HTML 비 허용시
$str4 = strip_tags($str); // HTML 태그가 포함될 경우 HTML 태그 자체를 삭제
echo $str4.'<br/>';


$str ="<script>alert('xss 공격!');</script>";
$str1 = strip_tags($str); // HTML 태그가 포함될 경우 HTML 태그 자체를 삭제
echo $str1.'<br/>';
$str2 = preg_replace("/(<([^>]+)>)/i","",$str);
echo $str2.'<br/>';

$str ='<a href="javascript:alert(\'xss 공격!\')">XSS</a>';
$str1 = strip_tags($str); // HTML 태그가 포함될 경우 HTML 태그 자체를 삭제
echo $str1.'<br/>';


$str ='<img src="#" onerror="alert(\'XSS 공격!\');">';
$str = strip_tags($str); // HTML 태그가 포함될 경우 HTML 태그 자체를 삭제
echo $str.'<br/>';

$str ='<IFRAME src="&#106;&#097;&#118;&#097;&#115;&#099;&#114;&#105;&#112;&#116;&#058;
&#097;&#108;&#101;&#114;&#116;&#040;&#039;&#120;&#115;&#115;&#032;&#234;&#179;&#181;
&#234;&#178;&#169;&#033;&#039;&#041;&#059;" width="0" height="0" frameborder="0"></IFRAME>';
$str = strip_tags($str); // HTML 태그가 포함될 경우 HTML 태그 자체를 삭제
echo $str.'<br/>';

?>


결과 화면


Text to ASCII Converter : http://www.unit-conversion.info/texttools/ascii/#data


정규표현식으로 HTML 태그를 제거하는 것은 Javascript 함수로 만들어서 확인할 수 있다.


<iframe> 태그 삽입, <object> 태그 삽입, Web 브라우저 URL Spoofing(변조), 악성코드 은닉 등 다양한 XSS 공격 기법이 가능하므로 필터링에 각별한 고려가 필요하다.


국내에서는 게시판이 대부분 WYSIWYG 에디터를 이용하므로 선택적 필터링을 잘 해야 한다.


HTML Purifier는 해외의 보안 전문가들로부터 철저하게 검증받은 필터링 라이브러리이다.
whitelisting 기법을 사용하고, 잘못된 태그도 확실하게 걸러내준다.
처리속도가 빠르지 않기 때문에 HTML이 필요한 경우에만 사용해야 한다.


참고하면 도움되는 게시글
ㅇ 안경잡이 개발자 XSS 공격 강의 : https://www.youtube.com/watch?v=DoN7bkdQBXU
ㅇ [웹취약점] XSS 크로스 사이트 스크립팅 http://coashanee5.blogspot.com/2017/02/xss.html
ㅇ KISA(한국인터넷진흥원) XSS 공격 종류 및 대응 방법 : http://www.kisa.or.kr/uploadfile/201312/201312161355109566.pdf


'Web 프로그램 > Web Security' 카테고리의 다른 글

HTML Purifier 사용법 예시  (0) 2023.02.28
remote IP address  (0) 2021.03.24
파일 다운로드 공격 방지  (0) 2019.07.05
파일 업로드 공격 방지  (0) 2019.06.21
SQL Injection 공격 방지  (1) 2019.06.19
블로그 이미지

Link2Me

,
728x90
SQL Injection

설명

웹 애플리케이션에서 사용되는 SQL 구문에 공격자가 임의의 구문을 주입(Injection) 하여 내부 데이터베이스의 데이터를 유출, 변조할 수 있는 취약점이다.

 발생원인

웹 페이지에서 사용자로부터 입력받은 값을 제대로 검사하지 않고 그대로 데이터 질의어로 사용할 경우 발생한다.

 위험성

데이터 유출, 변조 외에도 서버에 파일을 쓰거나 읽을 수 있고, 직접 명령 실행도 가능할 수 있기 때문에 위험도가 높은 취약점이다.


SQL 인젝션은 데이터베이스를 사용하는 모든 언어에서 일어날 수 있다.
공격자는 SQL 인젝션 취약점을 사용하여 데이터베이스 내의 모든 정보를 추출할 수 있다.
보통 userID 와 passwd 를 입력하는 로그인 창에서 발생할 수 있는 공격 유형이다.


select * from members where userID='$userID' and passwd ='$passwd';

여기서 userID 에 입력하는 정보를 ' or 1=1-- 라고 입력하는 것이 받아들여지면 어떤 결과가 초래될까?
phpMyAdmin 상에서 직접 테스트 해보라.


SELECT * FROM members WHERE userID='' or 1=1 --' and passwd ='abc123';

과같이 -- 주석 뒷부분은 무용지물이 되고 조건문은 무조건 참이 되어 모든 결과를 반환하게 된다.


select * from members where userID='a' and passwd ='password' OR 1='1';
로 입력해도 무조건 참이 되어 모든 결과를 반환하게 된다.


더 위험한 것은 테이블 자체를 삭제시켜 버릴 수가 있다는 점이다.

SELECT * FROM members WHERE userID = 'a';DROP TABLE members;

$userID = "admin";
$password = "password' OR 1='1 --";
$sql = "SELECT * FROM members WHERE userID='{$userID}' AND passwd='{$password}'";
echo 'NonFiltering : '.$sql.'<br/>';
$sql=preg_replace("/\s{1,}1\=(.*)+/","",$sql); // 공백이후 1=1이 있을 경우 제거
$sql=preg_replace("/\-{2,}/","",$sql); // 주석 제거
$sql=preg_replace("/\s{1,}(or|and|null|where|limit)/i"," ",$sql); // 공백이후 or, and 등이 있을 경우 제거
echo 'Filtering : '.$sql.'<br/>'; 

공백이후 and 를 체크하지 않고 그냥 체크하면 userID 중에 hand 가 들어간 것도 필터링을 해서 문제가 될 수 있다.


그럼 여기서 안전하게 코딩하기 위한 포인트는 무엇인가?

① 필터링 메소드를 만들어서 악의적인 코드를 무조건 걸러내도록 한다.

    필터링 해야 할 특수문자 : '(홀따옴표) ;(콜론), --(주석), 공백 등

    https://link2me.tistory.com/1489 정규표현식 연습 참조하면 정규표현식 이해에 도움된다.

    정규표현식을 활용한 함수 만들기

    function SQLFiltering($str){
        // 해킹 공격을 대비하기 위한 코드
        $str=preg_replace("/\s{1,}1\=(.*)+/","",$str); // 공백이후 1=1이 있을 경우 제거
        $str=preg_replace("/\s{1,}(or|and|null|where|limit)/i"," ",$str); // 공백이후 or, and 등이 있을 경우 제거
        $str = preg_replace("/[\s\t\'\;\=]+/","", $str); // 공백이나 탭 제거, 특수문자 제거
        return $str;
    }

②  안전한 코딩은 prepared Statement 와 바인딩 변수를 이용하도록 권고하고 있다.

      Prepared Statement 를 쓰면 SQL 인젝션 공격이 불가능할 수 밖에 없다.

      바인딩 데이터는 SQL 문법이 아닌 내부의 인터프리터나 컴파일 언어로 처리하기 때문에

      문법적인 의미를 가질 수 없다.

$stmt = $mysqli->prepare("SELECT * FROM members WHERE userID= ? AND passwd = ?");
$stmt->bind_param("ss", $_POST['userID'], $_POST['passwd']);
$stmt->execute();
//fetching result would go here, but will be covered later
$stmt->close();


참고하면 도움이 될 게시글

ㅇ PHP Prepared Statements https://www.w3schools.com/php/php_mysql_prepared_statements.asp
ㅇ PHP Legacy 함수를 PDO 방식 함수로 변환하기 https://link2me.tistory.com/1636

'Web 프로그램 > Web Security' 카테고리의 다른 글

HTML Purifier 사용법 예시  (0) 2023.02.28
remote IP address  (0) 2021.03.24
파일 다운로드 공격 방지  (0) 2019.07.05
파일 업로드 공격 방지  (0) 2019.06.21
Cross-Site Scripting (XSS) 방지  (0) 2019.06.20
블로그 이미지

Link2Me

,
728x90

How to alidate IP address with regular expression


IP 주소와 Subnet Mask 입력이 잘못된 것인지 체크하는 메소드를 작성해봤다.


import java.util.regex.Pattern;

public class Validation {

    public static void main(String[] args) {
       
        String inputValue = "10.10.10.256/21";
        String result = IPSubnetMask(inputValue);
        if(result.equals("1")){
            System.out.println("IP 주소와 서브넷 마스크를 정상적으로 입력했습니다.");
        } else if(result.equals("2")){
            System.out.println("ip 주소 입력이 잘못되었습니다.");
        } else if(result.equals("3")){
            System.out.println("서브넷 마스크가 맞나요?");
        } else {
            System.out.println("IP 주소와 서브넷 마스크 입력 정보를 확인하세요.");
        }       

    }
   
    private static String IPSubnetMask(String masArray){
        if(masArray.contains("/")){
            String[] input_ipsubnet = masArray.split("/"); // / 기호로 분리하라.
            String ipaddress = input_ipsubnet[0];
            String subnetMask = input_ipsubnet[1];
            System.out.println("ipaddress :" + ipaddress + ", subnetMask : " + subnetMask);
            if(IP_Validation.matcher(ipaddress).matches()) {
                if(Integer.parseInt(subnetMask)< 23) {
                    return "3"; // 서브넷 마스크 확인 필요
                } else {
                    return "1"; // 정상
                }               
            } else {
                return "2"; // IP주소 입력 체크
            }
        } else {
            return "0";
        }
    }
   
    private static final Pattern IP_Validation =
            Pattern.compile("^((25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[0-9])\\.){0,3}"+
                    "((25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[0-9])){0,1}$");
   
}
 





'안드로이드 > Java 문법' 카테고리의 다른 글

Java 추상 클래스  (0) 2019.08.19
[Java] 다형성  (0) 2019.06.30
자바 클래스 개념 이해 예제  (0) 2019.05.27
자바 Arraylist  (0) 2019.05.26
Java BubbleSort  (0) 2019.05.13
블로그 이미지

Link2Me

,
728x90

Android 앱을 개발한 후 배포를 하기 위해 알아야 할 사항이다.


앱 build.gradle 에서

    defaultConfig {
        applicationId "com.android.USBController"
        minSdkVersion 19
        targetSdkVersion 27
        versionCode 1
        versionName "1.0"


모든 Android 앱은 저마다 com.example.myapp과 같이 자바 패키지 이름처럼 보이는 고유한 애플리케이션 ID가 있다.
이 ID 덕분에 기기와 Google Play Store에서 각각의 앱을 고유하게 식별할 수 있는 것이다.
앱의 새로운 버전을 업로드하려면 애플리케이션 ID와 로그인할 때 사용할 인증서가 원래의 APK와 같아야 한다.
애플리케이션 ID를 변경할 경우 Google Play Store에서는 APK를 완전히 다른 앱으로 취급하게 된다.
따라서 일단 앱을 게시한 후에는 절대로 애플리케이션 ID를 변경하지 마시라.

앱을 개발할 때 기능을 약간 다르게 비교하면서 개발할 경우 applicationId 인 com.android.USBController 를 같게 하면 동일한 앱으로 컴파일된다는 걸 알 수 있다.
이 applicationId 만 다르게 하여 컴파일하면 두개의 앱이 생성되는 걸 확인할 수 있을 것이다.

PlayStore에 등록된 앱은 VersionCode와 VersionName의 두가지 속성을 가지고 있다.

VersionCode는 정수값을 이용하는데, 플레이 스토어 내부적으로 상위 버전을 구분하는데 사용되고, VersionName은 플레이 스토어에서 사용자에게 보여주기 위한 값으로 사용된다.


versionCode : 정수이며, 내부 버전 번호로 사용된다.
하나의 버전이 다른 버전보다 최신인지 여부를 판단하는 데만 사용되며, 번호가 높을수록 최신 버전이다.


versionName : 문자열이며, 사용자에게 표시되는 버전 번호로 사용된다.
사용자에게 표시하는 것 이외에 다른 용도는 없다.
따라서 별도로 시스템상으로 강제하고 있지 않다.


기업 내부용으로 이용시에는 신뢰할 수 없는 앱이라는 경고 표시가 나온다.

앱을 새롭게 업데이트해서 배포할 때마다

versionCode 와 versionName 을 숫자를 증가시켜야 오류가 발생하지 않는다.


아래 코드는 앱에서 현재 사용하는 버전이 어떤 버전인지 UI상에 표시해주는 코드이다.

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_menu);
        mContext = this.getBaseContext();
        backPressHandler = new BackPressHandler(this); // 뒤로 가기 버튼 이벤트

        // type1이 버튼형식
        findViewById(R.id.menu1).setOnClickListener(this);
        findViewById(R.id.menu2).setOnClickListener(this);
        findViewById(R.id.menu3).setOnClickListener(this);
        findViewById(R.id.menu4).setOnClickListener(this);

        TextView tv_version = findViewById(R.id.version);

        PackageInfo pInfo = null;
        try {
            pInfo = getPackageManager().getPackageInfo(this.getPackageName(), 0);
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
        int versionCode = pInfo.versionCode;
        String versionName = pInfo.versionName; // build.gradle 에 기록된 VersionName
        tv_version.setText("Version "+versionName);
    }


블로그 이미지

Link2Me

,
728x90

Ubiquoss L2 스위치 패스워드 설정법


모드

명령어

 (config)#

 username root password PASSWORD

 (config)#

 enable password PASSWORD

 (config)#

 service password-encryption


다산네트웍스 L2 스위치 패스워드 설정법

모드

명령어

 (config)#

 passwd

 (config)#

 passwd enable PASSWORD

 (config)#

 service password-encryption



블로그 이미지

Link2Me

,
728x90

오늘 모처럼 네이버 TV 중계로 잠깐 홍건희가 던지는 모습을 지켜봤다.

고교 1학년에 당시 감독이었던 이건열에게 권유받아 투수가 되었다고 기록을 찾아보니 나온다.

과연 투수가 적합한가 의심이 들 정도로 담력이 너무나도 약하고 스스로 무너지는 고질병을 고치지 못하는 한 투수를 하면 안되겠다는 생각이 들더라.

올해는 공인구 반발력도 낮아져서 자신있게 공을 뿌리고 마음껏 던져도 될텐데 스스로 무너진다.

아직 마음먹은 곳에 제구를 못하고, 더 큰 문제는 타자가 배트를 휘두르다가 데드볼이 된 상황이후 공을 모두 바깥 빠지는 공을 던지면서 볼넷으로 스스로 무너지고 견제구 던지다가 안줘도 될 점수 2점을 내주면서 경기를 팽팽하게 끌고가지 못하고 무너지고 말았다.

승부처에서 스스로 무너지는 고질적인 새가슴을 고치지 못할바엔 투수를 그만두는게 낫겠더라.

더군다나 군대도 갔다왔는데 절심함이라곤 눈꼽 만큼도 없어 보이더라.

스스로 무너지고 얼굴 새빨개지는 쫄보 홍건희는 각성을 하길 바라는 마음에 적어본다.

홍건희는 볼이 가벼운 편인거 같다.

LA 다저스 노장투수 리치 힐(39세)이 메이저리그에서 경쟁력을 가질 수 있는 것은 높은 회전수에 기반한 커브(회전수 2925회)와 포심(회전수 2477회)의 움직임이라고 한다.

볼 빠르기보다 볼 회전수가 높아야 할텐데 볼에 가벼우니까 승부처, 점점 상황에서 내 공이 맞아나가면 어떻하지 하는 스스로를 믿지 못하는 악순환이 반복되는 건 아닐까 싶은 생각도 든다.

원하는 곳에 정확하게 공을 던질 수 있는 제구력과 더불어 볼회전력을 키워야 한다.

손목 힘을 기르기 위해 세손가락으로 팔꿉혀펴기를 했다는 박찬호 투수처럼 정말 절심함으로 최고의 투수가 되어 보겠다는 일념이 없이 어쩌다 승리 한번 하고 무너지는 패턴을 반복하는 한 야구 그만두는게 정신건강에 좋을 거 같다.


오늘 경기는 박흥식 감독대행의 타순짜기에도 문제는 많아 보이더라.

터커는 아직 사이드암, 언더핸드 투수를 경험하지 못했기 때문에 당연히 약할 거라 보고서 하위타순으로 내리던가 해야 하는데 3번에서 병살을 치면서 경기의 흐름을 이어주지 못하더라.


최원준은 독하게 야구하는 모습이 없고, 태그도 과감하게 해야 하는데 소극적으로 하는 걸 보니까 아직 야구에 대한 절심함이 부족하구나 싶더라.

승부처에서 실책을 범하고, 악착같은 끈질김이 없이는 팀에서 1순위로 해당 포지션에서 살아남을 수 없다는 걸 알아야 하는데 아직은 그게 턱없이 부족하다.

내가 3루수로서는 최고가 되겠다는 자부심이 가져야 한다.


구단이 돈이 없어서 포스트 시즌 보너스 받겠다고 눈에 불을 키고 야구하는 해태 타이거즈의 강인함이 KIA 야구에서는 보이지 않는다.


2019.6.20 SK : KIA 5:8

7이닝동안 홍건희는 2실점으로 호투했다. 정의윤에게 7회 투런 홈런을 맞기 전까지는 완벽한 투구를 했고,

8회 전상현이 3점 실점하는 바람에 동점(중견수 이창진의 수비실수 포함)이 되어 승리 투수가 되지는 못했지만 팀이 이기는 경기에 일조했다.


2019.8.1 KIA : SK 10:1

홍건희는 1회부터 5점을 내주는 등 제구가 되지 않는 배팅볼 투수 수준이다.

제구력을 키우던지 정 안되면 ** 처럼 스핏볼이라도 던져보던가 더위에 헉헉대고, 제구는 안되고 얻어맞고 팬들은 다시는 보고 싶지 않다고, 댓글에 원성이 자자하다.

제발 각성하고 제구력 좀 키워라. 지금 1군에 있을 실력이 못된다.

블로그 이미지

Link2Me

,
728x90

LG G5 개발용 폰에서는 테스트하면 정상적으로 잘 설치된다.

그런데 갤럭시노트 8 에서 테스트를 하니까 제대로 설치가 안된다.

"앱이 설치되지 않았습니다." 라면서 설치가 되지 않는다.

 

release 모드로 APK 파일을 생성할 때

Signature Versions : V1 ← OS가 7.0 미만일 때

V2 ← OS가 7.0 이상일 때 체크해서 생성하면 되는데 둘다 체크해서 만들면 된다.

APK 파일이 잘못 생성될 수도 있으므로 프로젝트를 clean 한 후 다시 build한 후 APK를 생성한다.

 

구글은 PHA(유해한 앱) 앱의 비율을 차례대로 줄여 나갔으며, 비공식 마켓 등에서 배포되는 PHA 앱은 여전히 남아 있다.
안드로이드 8.0인 오레오(oreo)부터는 플레이 스토어 를 제외한 비공식 마켓에서 배포되는  앱을 설치할때는 새로운 권한을 얻도록 하였다.

 

1. 이미 동일한 패키지명의 앱이 설치 되어 있는 경우.

   - 이 경우에는 기존 설치된 어플의 흔적을 제거해주고 다시 설치하면 설치가 된다.

 

2. 구글 플레이 프로텍트에서 막은 경우

   - 구글 플레이 스토어 실행

   - 상단 왼쪽에 메뉴 아이콘을 클릭 후 Play 프로텍트 선택

   - "기기에 보안 위협이 있는지 검색" 항목 체크 해제

   - "유해한 앱을 감지하는 기능 보완" 항목 체크 해제

 

안드로이드 8.0 오레오에서 부터 '출처를 알 수 없는 앱' 정책에 변경됐다. 
구글 정책 변경에 따라 기존 방식인 '설정-해제' 방식은 사라지고 각 '앱 별 관리' 방식으로 바뀌게 되었다.

블로그 이미지

Link2Me

,
728x90

유벤투스 소속의 크리스티아누 호날두(1985년생)가 1년만에 A매치 헤드트릭을 했다.

포르투갈 축구대표팀의 호날두는 6일 오전 3시 45분(한국시각) 포르투갈 포르투의 에스타디우 두 드라가오서 열린 ‘2018-19 UEFA 네이션스리그’ 준결승에 선발 출전, 3골을 터뜨리며 스위스전 3-1 승리를 주도했다.
 
30대 중반에 접어든 호날두는 과거에 비해 현란한 드리블이나 체력 소모가 큰 움직임 보다는 골을 넣을 수 있는 최소한의 세밀한 플레이를 바탕으로 놀라운 결정력을 뽐냈다.


전반 25분 자신이 얻어낸 프리킥의 키커로 나선 호날두는 무회전 슈팅으로 선제골을 터뜨렸다.


https://sports.news.naver.com/wfootball/vod/index.nhn?firstVid546665=&id=547834&autoPlay=true&category=wfootball&listType=total



후반 43분 EPL 맨체스터 시티 소속의 세계 최고의 미드필더 중 한명인 베르나르두 실바의 땅볼 크로스를 논스톱 슈팅으로 두번째 골을 득점했다.

1분 뒤에는 역습 상황에서 수비수를 제치고 강력한 오른발 슈팅으로 쐐기골을 터뜨려 헤드트릭을 달성했다.


결승에 진출한 포르투갈은 네덜란드-잉글랜드 승자와 우승컵을 두고 다툰다.


제 2의 호날두라 불리던 주앙 펠릭스의 데뷔전은 아쉬움이 남았다. 호날두와 함께 투톱으로 나섰지만 70분 동안 단 1개의 슈팅을 시도하는데 그쳤다. 그는 평점 5.9점으로 포르투갈 선수 중 가장 낮은 점수를 받았다.



베르나르두 실바

맨체스터 시티(맨시티)가 올(2019년) 시즌 팀 전력의 핵심으로 떠오른 베르나르두 실바(24)와 3월에 2025년까지 재계약을 맺었다.
티키 베기리스타인 맨시티 이사는 "베르나르두(실바)는 특출난 재능을 보유했다"며, "그에게 장기 계약을 제시하는 건 우리에게 매우 쉬운 결정이었다.
그와의 재계약은 맨시티가 젊으면서도 세계 최정상급 선수단을 구축하는 데 집중하는 팀이라는 사실을 증명한다.
우리는 지속된 성공을 위한 기반을 만들고 싶다. 베르나르두는 이 계획에 부합하는 선수"라고 설명했다.


스피드와 테크닉, 높은 축구 지능을 가지고 있는 선수로 평가 받는 베르나르두 실바는 1994년 8월 10일 리스본에서 태어나 2002년 SL 벤피카 유스에 입단하여 처음 축구화를 신었다.

그후 2013년 정식으로 프로 데뷔를 했지만 1군에서는 단 1경기 밖에 뛰지 못했고 거의 대부분을 2군에서 뛰기만 했다.

본격적으로 베르나르두 실바라는 이름을 세계에 알린 것은 2016/17시즌.
실바는 음바페, 바카요코, 르마 등 동료들과 함께 모나코에 17년 만의 리그 우승 트로피를 안겼고
모든 대회 58경기에 출전해 11골 12어시스트를 기록, 시즌 종료 후 이적시장의 가장 뜨거운 감자가 되었다.

2017년 7월 실바는 평소 우상으로 여겼던 펩 과르디올라가 지도하고 있는 맨체스터 시티로 이적했다.


'스포츠 > 유럽 축구' 카테고리의 다른 글

토트넘 홋스퍼와 크리스탈 팰리스전  (0) 2019.09.15
블로그 이미지

Link2Me

,
728x90

스마트폰 앱을 다운로드하는 코드이다.

파일 다운로드하는 코드에 스마트폰 장치를 인식하는 코드와 접목하여 어플을 다운로드 하도록 한다.

파일을 직접 접속하는 경우에는 동작하지 않도록 $_SERVER['HTTP_REFERER'] 기능을 사용하여 체크한다.

좀 더 정교하게 보완하는 것도 좋을 것이다.


<?php
require_once "deviceChk.php"; // 접속 Device 체크
if($mtype > 2) { // PC에서 접속하면 아래 코드 미 실행
    echo 'no access';
    exit;
}
require_once 'config/config.php';
if($mtype==2){
    $url='<a href="appDown.php">ABC <br/>어플 다운로드</a>';
}
?>

<!DOCTYPE html>
<html lang="ko">
<head>
  <title>ABC App 다운로드</title>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.0/jquery.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"></script>
  <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"></script>
</head>
<body>
<div class="container">
    <div class="row align-items-center justify-content-center" style="height:60vh;">
        <div>
            <h4><?php echo $url;?></h4>
        </div>
    </div>
</div>
</body>
</html>

=== appDown.php ===

<?php
if(!isset($_SERVER['HTTP_REFERER'])) {
    echo 'direct access denied!';
    exit;
}

require_once "deviceChk.php"; // 접속 Device 체크
if($mtype==2){ // Android 폰 이면
    require_once 'config/config.php';
    $filepath = './files/'.$hostAbb.'.apk';
    $filesize = filesize($filepath);
    $path_parts = pathinfo($filepath);
    $filename = $path_parts['basename'];
    $extension = $path_parts['extension'];

    header("Pragma: public");
    header("Expires: 0");
    header("Content-Type: application/octet-stream");
    header("Content-Disposition: attachment; filename='$filename'");
    header("Content-Transfer-Encoding: binary");
    header("Content-Length: $filesize");
    header("Cache-Control: cache, must-revalidate");
    header("Pragma: no-cache");

    ob_clean();
    flush();
    //readfile($filepath); //Read and stream the file
    ///*
    $fp = fopen($filepath, "rb"); //rb 읽기전용 바이러니 타입
    if(!fpassthru($fp)) {
        fclose($fp);
    }
    //*/
}
?>


위 다운로드 코드에 약간 문제가 있는지 다운로드가 제대로 되지 않는 폰이 있어서 코드를 다시 수정했다.

아래 코드는 정상적으로 잘 다운로드 된다. 여러 폰에서 테스트를 했다. (update 2019.6.18)

<?php
if(!isset($_SERVER['HTTP_REFERER'])) {
    echo 'direct access denied!';
    exit;
}

require_once "deviceChk.php"; // 접속 Device 체크
if($mtype==2){ // Android 폰 이면
    require_once 'config/config.php';
    $filepath = './files/'.$hostAbb.'.apk';
    $filesize = filesize($filepath);
    $path_parts = pathinfo($filepath);
    $filename = $path_parts['basename'];
    $extension = $path_parts['extension'];

   header('Content-Description: File Transfer');
   header('Content-Type: application/octet-stream');
   header('Content-Disposition: attachment; filename="' . $filename . '"');
   header("Content-Transfer-Encoding: Binary");
   header('Expires: 0');
   header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
   header('Pragma: public');
   header("Content-length: ".filesize($filepath));
   ob_clean();
   flush();
   readfile($filepath);
}
?>


블로그 이미지

Link2Me

,
728x90

파일 구조를 분리하면서 AndroidManifest.xml 파일을 다음과 같이 했더니 파일이 두개가 설치되는 문제점이 있다.


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.android.USBController">

    <uses-feature android:name="android.hardware.usb.host" />

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.CALL_PHONE" />

    <application
        android:allowBackup="false"
        android:icon="@drawable/icon"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme.NoActionBar">
        <activity
            android:name=".Intro"
            android:screenOrientation="portrait">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name=".MainActivity"
            android:launchMode="singleTask"
            android:screenOrientation="portrait">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
            </intent-filter>

            <meta-data
                android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
                android:resource="@xml/device_filter" />
        </activity>
        <activity android:name=".Setting" />
        <activity android:name=".ShortKey" />
        <activity android:name=".Upgrade" />
    </application>

</manifest>
 


시작되는 파일을 변경처리 했더니 위와 같이

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

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
부분이 두개가 생겼는데 그냥 컴파일 했더니 파일이 두개가 생긴다.

반드시 하나를 지워줘야 한다.

그래서 MainActivity 부분에 있는 걸 지워줬다.

이유는 위험권한 체크하는 부분을 Intro 로 옮겼는데 Intro 부분걸 지우면 권한 체크를 하지 못한다.

당연한 얘기로 처리 순서와 관련된 사항이다.

가장 먼저 Intro 파일을 실행하고 파일에서 내용을 확인한 후 MainActivity 로 이동하거나 Upgrade 로 이동한다.

블로그 이미지

Link2Me

,
728x90

콘솔 앱은 UI 전환없이 UI내에서 아이콘 배치를 효율적으로 해야 이용하는데 불편함이 적다.

화면 전환없이 정보를 보여주는 것을 처리하기 위해서 팝업 메뉴를 사용하고, 도움말 기능은 팝업 메뉴로는 해결할 수가 없어서 팝업 윈도우 기능을 이용하여 구현했다.


팝업 윈도우 기능은 활용하기에 따라 유용한 점이 많을 거 같다.

기본적으로 PopupWindow 자체적으로 show(), dismiss(), setFocusable() 등의 메서드를 제공해 준다.


popup_window.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/linear_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="#ffffff">

    <WebView
        android:id="@+id/popupwebView"
        android:layout_width="match_parent"
        android:layout_height="400dp"
        android:background="#eee9e9" />

    <ImageButton
        android:id="@+id/btn_close"
        android:layout_width="31dp"
        android:layout_height="31dp"
        android:layout_marginTop="10dp"
        android:layout_marginRight="15dp"
        android:layout_gravity="right"
        android:background="@android:color/transparent"
        android:src="@drawable/btn_close" />

</LinearLayout>


버튼을 클릭하면 팝업창이 뜨는 구조이므로 View view 에서 동작한다.

HTML 파일을 bootstrap 기반으로 만들었고, 이걸 가장 잘 보여줄 수 있는 것은 WebView 라서 WebView 기능의 일부를 구현하여 팝업창이 구현되도록 했다.

URL 만 변경하면 다른 내용도 보여줄 수 있도록 함수화를 했다.

팝업창 외부 터치시 팝업창이 사리지도록 옵션을 설정했다.


case R.id.btnHelp:
   // 설정방법 설명
   mPopupWindowShow(view,Value.IPADDRESS + "/help.html");
   break;


public void mPopupWindowShow(View view, String url) {
    mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
    Display display = mWindowManager.getDefaultDisplay();
    //int width = LinearLayout.LayoutParams.MATCH_PARENT; // 100% 너비를 사용할 때
    int width = (int) (display.getWidth() * 0.9); //Display 사이즈의 90%
    int height = LinearLayout.LayoutParams.WRAP_CONTENT;
    // LayoutParams WRAP_CONTENT를 주면 inflate된 View의 사이즈 만큼의 PopupWinidow를 생성한다.

    // inflate the layout of the popup window
    LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
    LinearLayout layout = (LinearLayout) inflater.inflate(R.layout.popup_window,null);
    mPopupView = layout.findViewById(R.id.linear_layout);

    WebView webView = (WebView) layout.findViewById(R.id.popupwebView);
    ImageButton btn_close = layout.findViewById(R.id.btn_close);

    // create the popup window
    boolean focusable = true; // lets taps outside the popup also dismiss it
    final PopupWindow popupWindow = new PopupWindow(mPopupView, width, height, focusable);

    WebSettings webSettings = webView.getSettings();
    webSettings.setDefaultFontSize(9);
    webSettings.setCacheMode(WebSettings.LOAD_NO_CACHE);
    webView.loadUrl(url);
    webView.setWebViewClient(new WebViewClient() {
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            view.loadUrl(url);
            return true;
        }
    });

    popupWindow.showAtLocation(view, Gravity.CENTER, 0, 0); // Parent View 로부터의 위치


    btn_close.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            popupWindow.dismiss();
        }
    });
}


 mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
 Display display = mWindowManager.getDefaultDisplay();

 Point point = new Point();
 display.getSize(point);
 int width = (int) (point.x * 0.9); // Display 사이즈의 90%
 //int width = LinearLayout.LayoutParams.MATCH_PARENT;
 //int height = (int) (point.y * 0.8);
 int height = LinearLayout.LayoutParams.WRAP_CONTENT;
      

'안드로이드 > Android Serial 통신' 카테고리의 다른 글

Geateway 파싱 처리  (0) 2020.05.23
AlertDialog EditText  (0) 2019.06.26
android handler 를 이용한 지연처리  (0) 2019.05.09
Serial Communication Key  (0) 2019.05.09
Spinner 기능을 이용한 세팅 코드 구현  (0) 2019.05.02
블로그 이미지

Link2Me

,