添加验证码可以扰乱攻击者使用机器暴力破解口令
目录
效果图:
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>