Android 验证码

添加验证码可以扰乱攻击者使用机器暴力破解口令

目录

CodeUtils.java

GenerateCode.java

activity_generate_code.xml

输入栏与按钮的背景设置

linear_code.xml (输入栏背景设置)

button_code.xml(按钮背景设置)


效果图:

CodeUtils.java

生成图片验证码的工具类

package com.example.graphicimage;

import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;

import java.util.Random;
/*
验证码图片 = 位图 =
                验证码(随机生成不同的验证码)
                + 文本样式(随机生成不同的文本样式)
                + 间距(随机生成不同的间距)
                + 干扰线(随机生成不同的干扰线)
 */
public class CodeUtils {
    private static final char[] chars = {
            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
            'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
            'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
            'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
            'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
    };
    private static CodeUtils codeUtils;
    private int paddingLeft, paddingTop;
    private StringBuilder builder = new StringBuilder();
    private Random random = new Random();

    private static final int DEFAULT_CODE_LENGTH = 6;   //验证码的位数
    private static final int DEFAULT_TEXT_SIZE = 60;    //字体大小
    private static final int DEFAULT_LINE_LENGTH = 3;   //干扰线的条数
    private static final int BASE_PADDING_LEFT = 20;    //左边距
    private static final int RANGE_PADDING_LEFT = 30;   //左边距的范围值
    private static final int BASE_PADDING_TOP = 70;     //上边距
    private static final int RANGE_PADDING_TOP = 15;    //上边距的范围值
    private static final int DEFAULT_WIDTH = 300;       //图片宽度
    private static final int DEFAULT_HEIGHT = 100;      //图片高度
    private static final int DEFAULT_COLOR = 0xDF;      //背景颜色值
    private String code;                                //存储验证码

    public String getCode() {
        return code;
    }

    public static CodeUtils getInstance() {
        if (codeUtils == null) {
            codeUtils = new CodeUtils();
        }
        return codeUtils;
    }

    //生成验证码图片(位图)
    public Bitmap createBitmap() {
        //创建位图 -> 根据位图创建画布 -> 设置画布的颜色 -> 创建画笔 -> 设置不规则的验证码(样式、间距) -> 添加干扰线
        paddingLeft = 0;    //初始化
        paddingTop = 0;     //初始化
        //创建位图
        Bitmap bitmap = Bitmap.createBitmap(DEFAULT_WIDTH , DEFAULT_HEIGHT, Bitmap.Config.ARGB_8888);

        Canvas canvas = new Canvas(bitmap);                                     //创建画布
        RectF rectF = new RectF(0,0,DEFAULT_WIDTH,DEFAULT_HEIGHT);     //构建RectF对象存放矩形坐标
        Paint paint = new Paint();                                              //创建画笔
        paint.setTextSize(DEFAULT_TEXT_SIZE);                                   //设置字体大小
        paint.setColor(Color.rgb(DEFAULT_COLOR,DEFAULT_COLOR,DEFAULT_COLOR));   //设置画笔颜色
        canvas.drawRoundRect(rectF, 18, 18, paint);                      //绘制圆角矩形
//        canvas.drawColor(Color.rgb(DEFAULT_COLOR,DEFAULT_COLOR,DEFAULT_COLOR)); //设置画布颜色

        code = createCode();                    //创建验证码
        //绘制验证码
        for (int i = 0; i < code.length(); i ++) {
            randomTextStyle(paint);     //获取随机的字体样式
            randomPadding();            //获取随机的字体间距
            canvas.drawText(code.charAt(i)+"",paddingLeft,paddingTop,paint); //绘制验证码
        }
        //绘制干扰线
        for (int i = 0; i < DEFAULT_LINE_LENGTH; i ++) {
            drawLine(canvas , paint);   //调用随机干扰线方法,绘制不规则干扰线
        }

        //二者配套使用
        canvas.save();      //保存
        canvas.restore();   //恢复,防止原canvas上的图,影响新的canvas上的图
        return bitmap;
    }

    //生成验证码
    private String createCode() {
        builder.delete(0,builder.length()); //使用之前首先清空内容
        for (int i = 0; i < DEFAULT_CODE_LENGTH; i ++) { //利用chars字符数组随机产生含有数字与字母的验证码
            builder.append(chars[ random.nextInt(chars.length) ]);
        }
        return builder.toString();
    }

    //随机干扰线
    private void drawLine(Canvas canvas, Paint paint) {
        int color = randomColor();                          //调用随机颜色方法,生成随机颜色
        int startX = random.nextInt(DEFAULT_WIDTH);         //随机开始位置(X1)
        int startY = random.nextInt(DEFAULT_HEIGHT);        //随机开始位置(Y1)
        int stopX = random.nextInt(DEFAULT_WIDTH);          //随机结束位置(X2)
        int stopY = random.nextInt(DEFAULT_HEIGHT);         //随机结束位置(Y2)
        paint.setColor(color);                              //设置干扰线颜色
        paint.setStrokeWidth(1);                            //设置线条大小
        canvas.drawLine(startX,startY,stopX,stopY,paint);   //绘画线条
    }

    //随机文本样式(单个字体的样式)
    private void randomTextStyle(Paint paint) {
        //随机颜色、粗体、倾斜
        int color = randomColor();                      //调用随机颜色方法,生成随机颜色
        paint.setColor(color);                          //设置字体颜色
        paint.setFakeBoldText(random.nextBoolean());    //随机true,false, true粗体,false非粗体
        int SkewX = random.nextInt(11)/10;       //随机倾斜度
        SkewX = random.nextBoolean() ? SkewX:-SkewX;    //随机倾斜方向
        paint.setTextSkewX(SkewX);  //float类型参数,负数表示右斜,整数左斜
    }

    //随机颜色
    private int randomColor() {
        builder.delete(0, builder.length()); 使用之前首先清空内容
        String haxString;
        for (int i = 0; i < 3; i ++) {
            //随机生成16进制的数,转换成16进制的字符串
            haxString = Integer.toHexString(random.nextInt(0xFF));
            if (haxString.length() == 1) {  //不足两位补'0',保证每次随机产生两位字符
                haxString = "0" + haxString;
            }
            builder.append(haxString); //将产生的随机字符连接在一起组成从而表示16进制的颜色码
        }
        return Color.parseColor("#"+builder.toString()); //用颜色解析器将颜色字符串解析成颜色码
    }

    //随机间距(让验证码不规则排列)
    private void randomPadding() {
        paddingLeft += BASE_PADDING_LEFT + random.nextInt(RANGE_PADDING_LEFT);
        paddingTop = BASE_PADDING_TOP + random.nextInt(RANGE_PADDING_TOP);
    }
}

GenerateCode.java

活动类用于加载布局文件,验证码要设置成动态变化的,不然没啥用处

package com.example.graphicimage;

import androidx.appcompat.app.AppCompatActivity;

import android.graphics.Bitmap;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

public class GenerateCode extends AppCompatActivity implements View.OnClickListener {

    private CodeUtils codeUtils;
    private TextView txt_code;
    private ImageView img_code;
    private Button bt_commit;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_generate_code);
        setBackground();
        init();
    }

    private void init() {
        txt_code = findViewById(R.id.txt_code);
        img_code = findViewById(R.id.img_code);
        bt_commit = findViewById(R.id.bt_commit);
        //设置验证码图片
        codeUtils = CodeUtils.getInstance();
        Bitmap bitmap = codeUtils.createBitmap();
        img_code.setImageBitmap(bitmap);
        //监听提交按钮与验证码图片
        bt_commit.setOnClickListener(this);
        img_code.setOnClickListener(this);
    }

    private void setBackground() {
        getWindow().setStatusBarColor(getColor(R.color.status));
        getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.img_code: {   //点击验证码图片时更新验证码
                Bitmap bitmap = codeUtils.createBitmap();
                String code = codeUtils.getCode();
                Log.i("验证码",code);
                img_code.setImageBitmap(bitmap);
                break;
            }
            case R.id.bt_commit: {  //当出现验证码为空或者验证码错误时要及时更新验证码(防止机器暴力破解口令)
                String Code = txt_code.getText().toString();
                if (TextUtils.isEmpty(Code)) {  //核查验证码是否为空
                    Toast.makeText(this, "验证码不能为空", Toast.LENGTH_SHORT).show();
                    //设置验证码动态变化
                    Bitmap bitmap = codeUtils.createBitmap();
                    img_code.setImageBitmap(bitmap);
                    break;
                }
                String code = codeUtils.getCode();
                if (Code.equals(code)) { //查验输入的验证码是否正确
                    Toast.makeText(this, "验证码正确", Toast.LENGTH_SHORT).show();
                    //设置验证码动态变化
                    Bitmap bitmap = codeUtils.createBitmap();
                    img_code.setImageBitmap(bitmap);
                } else {
                    Toast.makeText(this, "验证码不正确", Toast.LENGTH_SHORT).show();
                    //设置验证码动态变化
                    Bitmap bitmap = codeUtils.createBitmap();
                    img_code.setImageBitmap(bitmap);
                }
                break;
            }
        }
    }
}

activity_generate_code.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/white"
    android:orientation="vertical">

    <TextView
        android:id="@+id/textView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="8dp"
        android:gravity="center"
        android:textColor="@color/black"
        android:textSize="21sp"
        android:text="验证码" />

    <View
        android:background="@color/black"
        android:layout_width="match_parent"
        android:layout_height="1dp"/>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <RelativeLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true">

            <LinearLayout
                android:id="@+id/Linear_code"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:background="@drawable/linear_code"
                android:orientation="horizontal"
                android:paddingLeft="6dp"
                android:paddingTop="5dp"
                android:paddingRight="6dp"
                android:paddingBottom="5dp">

                <EditText
                    android:id="@+id/txt_code"
                    android:layout_width="wrap_content"
                    android:layout_height="48dp"
                    android:layout_gravity="center"
                    android:gravity="center"
                    android:ems="10"
                    android:hint="请输入验证码"
                    android:inputType="textPersonName"
                    android:textColor="@color/black"
                    android:textColorHint="#6C6C6A"
                    android:textSize="18sp" />

                <ImageView
                    android:id="@+id/img_code"
                    android:layout_width="120dp"
                    android:layout_height="48dp"
                    android:layout_gravity="center" />

            </LinearLayout>

            <Button
                android:id="@+id/bt_commit"
                android:layout_width="match_parent"
                android:layout_height="48dp"
                android:layout_below="@+id/Linear_code"
                android:layout_marginTop="10dp"
                android:layout_alignLeft="@+id/Linear_code"
                android:layout_alignRight="@+id/Linear_code"
                android:background="@drawable/button_code"
                android:textColor="@color/black"
                android:textSize="18sp"
                android:text="提         交" />
        </RelativeLayout>

    </RelativeLayout>

</LinearLayout>

输入栏与按钮的背景设置

背景文件创建步骤:

        drawable  ->  new  ->  Drawable Resource File (名字自取,Root element 选 shape)

linear_code.xml (输入栏背景设置)

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="#F6EBD8"/>
    <corners android:radius="12dp"/>
    <stroke
        android:color="@color/black"
        android:width="1dp"/>
</shape>

button_code.xml(按钮背景设置)

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="#92F6EBD8"/>
    <corners android:radius="18dp"/>
</shape>

猜你喜欢

转载自blog.csdn.net/qq_51936803/article/details/130538287