• Java】实现图片验证码2.0【详细代码】


            实际开发过程中经常遇到要实现图片验证码来防止外部使用脚本刷接口,所以说图片验证码是很有必要的一个小功能。

    下面这个之前发布的,现在发现生成的图片验证码是可以被自动化工具进行识别的,具有一定的安全性问题。

    1.0版本验证码:

    【Java】实现图片验证码【详细代码】_图片验证码发送axios怎么弄-CSDN博客

    所以在2.0版本使用不同的方式实现图片验证码。

    2.0版本验证码:

    前端login.tsx:只提供了关键的代码。

    1. const LoginPage = () => {
    2. const [image, setImage] = useState("");
    3. const [captchaVal, setCaptchaVal] = useState("");
    4. const [captchaKey, setCaptchaKey] = useState("");
    5. const [captchaLoading, setCaptchaLoading] = useState(true);
    6. const fetchImageCaptcha = () => {
    7. setCaptchaVal("");
    8. setCaptchaLoading(true);
    9. system.getImageCaptcha().then((res: any) => {
    10. setImage(res.data.image);
    11. setCaptchaKey(res.data.key);
    12. setCaptchaLoading(false);
    13. });
    14. };
    15. useEffect(() => {
    16. fetchImageCaptcha();
    17. }, []);
    18. return (
    19. <div className={styles["login-content"]}>
    20. <div className={styles["login-box"]}>
    21. <div className={styles["right-box"]}>
    22. <div className="login-box d-flex mt-50">
    23. <Input
    24. value={email}
    25. onChange={(e) => {
    26. setEmail(e.target.value);
    27. }}
    28. style={{ width: 400, height: 54 }}
    29. placeholder="请输入管理员邮箱账号"
    30. onKeyUp={(e) => keyUp(e)}
    31. allowClear
    32. />
    33. div>
    34. <div className="login-box d-flex mt-50">
    35. <Input.Password
    36. value={password}
    37. onChange={(e) => {
    38. setPassword(e.target.value);
    39. }}
    40. allowClear
    41. style={{ width: 400, height: 54 }}
    42. placeholder="请输入密码"
    43. />
    44. div>
    45. <div className="d-flex mt-50">
    46. <Input
    47. value={captchaVal}
    48. style={{ width: 260, height: 54 }}
    49. placeholder="请输入图形验证码"
    50. onChange={(e) => {
    51. setCaptchaVal(e.target.value);
    52. }}
    53. allowClear
    54. onKeyUp={(e) => keyUp(e)}
    55. />
    56. <div className={styles["captcha-box"]}>
    57. {captchaLoading && (
    58. <div className={styles["catpcha-loading-box"]}>
    59. <Spin size="small" />
    60. div>
    61. )}
    62. {!captchaLoading && (
    63. <img
    64. className={styles["captcha"]}
    65. onClick={fetchImageCaptcha}
    66. src={image}
    67. />
    68. )}
    69. div>
    70. div>
    71. <div className="login-box d-flex mt-50">
    72. <Button
    73. style={{ width: 400, height: 54 }}
    74. type="primary"
    75. onClick={loginSubmit}
    76. loading={loading}
    77. >
    78. 立即登录
    79. Button>
    80. div>
    81. div>
    82. div>
    83. div>
    84. );
    85. };

    前端接口:

    1. export function getImageCaptcha() {
    2. return client.get("/backend/system/image-captcha", {});
    3. }

    引入依赖:

    1. <dependency>
    2. <groupId>com.github.pengglegroupId>
    3. <artifactId>kaptchaartifactId>
    4. <version>2.3.2version>
    5. dependency>

    后端Controller:

    1. @RestController
    2. @RequestMapping("/backend/system")
    3. @Slf4j
    4. public class SystemController {
    5. @Autowired private ImageCaptchaService imageCaptchaService;
    6. //验证码
    7. @GetMapping("/image-captcha")
    8. public JsonResponse imageCaptcha() throws IOException {
    9. ImageCaptchaResult imageCaptchaResult = imageCaptchaService.generate();
    10. HashMap<String, String> data = new HashMap<>();
    11. data.put("key", imageCaptchaResult.getKey());
    12. data.put("image", imageCaptchaResult.getImage());
    13. return JsonResponse.data(data);
    14. }
    15. }

    Service:

    1. public interface ImageCaptchaService {
    2. ImageCaptchaResult generate() throws IOException;
    3. boolean verify(String key, String code);
    4. }

    ImageCaptchaResult实体类:

    1. @Data
    2. public class ImageCaptchaResult {
    3. //图形验证码的key
    4. public String key;
    5. public String image;
    6. public String code;
    7. }

    ImageCaptchaServiceImpl:

    1. @Slf4j
    2. @Service
    3. public class ImageCaptchaServiceImpl implements ImageCaptchaService {
    4. @Value("${edu.captcha.cache-prefix}")
    5. private String ConfigCachePrefix;
    6. @Value("${edu.captcha.expire}")
    7. private Long ConfigExpire;
    8. @Resource
    9. private DefaultKaptcha defaultKaptcha;
    10. @Override
    11. public ImageCaptchaResult generate() throws IOException {
    12. ImageCaptchaResult imageCaptcha = new ImageCaptchaResult();
    13. BufferedImage image;
    14. // 图形验证码的key[api是无状态的需要key来锁定验证码的值]
    15. String randomKey = HelperUtil.randomString(16);
    16. imageCaptcha.setKey(randomKey);
    17. // 生成验证码
    18. imageCaptcha.setCode(defaultKaptcha.createText());
    19. image = defaultKaptcha.createImage(imageCaptcha.getCode());
    20. // 写入到redis中
    21. RedisUtil.set(getCacheKey(randomKey), imageCaptcha.getCode(), ConfigExpire);
    22. FastByteArrayOutputStream os = new FastByteArrayOutputStream();
    23. ImageIO.write(image, "png", os);
    24. String base64 = "data:image/png;base64," + Base64Util.encode(os.toByteArray());
    25. imageCaptcha.setImage(base64);
    26. return imageCaptcha;
    27. }
    28. }

    application.ymal: @Value注解需要读取到的

    1. edu:
    2. # 图形验证码
    3. captcha:
    4. expire: 300 #有效期[单位:秒,默认5分钟]
    5. cache-prefix: "captcha:key:" #存储key的前缀

    RedisUtil:

    1. import jakarta.annotation.Resource;
    2. import org.springframework.data.redis.connection.RedisConnection;
    3. import org.springframework.data.redis.core.Cursor;
    4. import org.springframework.data.redis.core.RedisTemplate;
    5. import org.springframework.data.redis.core.ScanOptions;
    6. import org.springframework.data.redis.serializer.RedisSerializer;
    7. import org.springframework.stereotype.Component;
    8. import org.springframework.util.CollectionUtils;
    9. import java.util.*;
    10. import java.util.concurrent.TimeUnit;
    11. @Component
    12. public class RedisUtil {
    13. private static RedisTemplate redisTemplate;
    14. private static final String redisPrefix = "EDU";
    15. /**
    16. * 注入Redis
    17. *
    18. * @param redisTemplate Redis对象
    19. * @author fzr
    20. */
    21. @Resource
    22. public void setRedisTemplate(RedisTemplate redisTemplate) {
    23. RedisUtil.redisTemplate = redisTemplate;
    24. }
    25. /**
    26. * 对象句柄
    27. *
    28. * @return RedisTemplate
    29. * @author fzr
    30. */
    31. public static RedisTemplate handler() {
    32. return redisTemplate;
    33. }
    34. /**
    35. * 指定缓存失效时间
    36. *
    37. * @param key 键
    38. * @param second 时间(秒)
    39. * @author fzr
    40. */
    41. public static void expire(String key, Long second) {
    42. key = redisPrefix + key;
    43. redisTemplate.expire(key, second, TimeUnit.SECONDS);
    44. }
    45. /**
    46. * 指定缓存失效时间
    47. *
    48. * @param key 键
    49. * @param millisecond 时间(毫秒)
    50. * @author fzr
    51. */
    52. public static void pExpire(String key, Long millisecond) {
    53. key = redisPrefix + key;
    54. redisTemplate.expire(key, millisecond, TimeUnit.MILLISECONDS);
    55. }
    56. /**
    57. * 指定缓存永久有效
    58. *
    59. * @param key 键
    60. * @author fzr
    61. */
    62. public static void persist(String key) {
    63. key = redisPrefix + key;
    64. redisTemplate.persist(key);
    65. }
    66. /**
    67. * 根据key获取过期时间
    68. *
    69. * @param key 键不能为null
    70. * @return 返回0代表为永久有效(秒)
    71. * @author fzr
    72. */
    73. public static Long ttl(String key) {
    74. key = redisPrefix + key;
    75. return redisTemplate.getExpire(key, TimeUnit.SECONDS);
    76. }
    77. /**
    78. * 根据key获取过期时间
    79. *
    80. * @param key 键不能为null
    81. * @return 返回0代表为永久有效(毫秒)
    82. * @author fzr
    83. */
    84. public static Long pTtl(String key) {
    85. key = redisPrefix + key;
    86. return redisTemplate.getExpire(key, TimeUnit.MILLISECONDS);
    87. }
    88. /**
    89. * 判断key是否存在
    90. *
    91. * @param key 键
    92. * @return true=存在,false=不存在
    93. * @author fzr
    94. */
    95. public static Boolean exists(String key) {
    96. key = redisPrefix + key;
    97. return redisTemplate.hasKey(key);
    98. }
    99. /**
    100. * 删除1个或多个键
    101. *
    102. * @param key 键(一个或多个)
    103. * @author fzr
    104. */
    105. @SuppressWarnings("unchecked")
    106. public static void del(String... key) {
    107. if (key.length == 1) {
    108. key[0] = redisPrefix + key[0];
    109. redisTemplate.delete(key[0]);
    110. } else {
    111. for (int i = 0; key.length > i; i++) {
    112. key[i] = redisPrefix + key[i];
    113. }
    114. redisTemplate.delete((Collection) CollectionUtils.arrayToList(key));
    115. }
    116. }
    117. /**
    118. * 给key赋值一个新的key名
    119. *
    120. * @param oldKey 旧的key
    121. * @param newKey 新的key
    122. * @author fzr
    123. */
    124. public static void rename(String oldKey, String newKey) {
    125. oldKey = redisPrefix + oldKey;
    126. newKey = redisPrefix + newKey;
    127. redisTemplate.rename(oldKey, newKey);
    128. }
    129. /**
    130. * 将当前数据库的key移动到给定的数据库db当中
    131. *
    132. * @param key 键
    133. * @param db 库
    134. * @return Boolean
    135. * @author fzr
    136. */
    137. public static Boolean move(String key, int db) {
    138. key = redisPrefix + key;
    139. return redisTemplate.move(key, db);
    140. }
    141. /**
    142. * 获取匹配的key值
    143. *
    144. * @param pattern 通配符(*, ?, [])
    145. * @return Set
    146. * @author fzr
    147. * @author fzr
    148. */
    149. public static Set keys(String pattern) {
    150. return redisTemplate.keys(pattern);
    151. }
    152. /**
    153. * 随机返回一个key
    154. *
    155. * @return String
    156. * @author fzr
    157. * @author fzr
    158. */
    159. public static String randomKey() {
    160. return redisTemplate.randomKey();
    161. }
    162. /* ***************** common end *************** */
    163. /**
    164. * 按匹配获取或有KEY
    165. *
    166. * @param pattern 规则
    167. * @return Set
    168. * @author fzr
    169. */
    170. public static Set matchSet(String pattern) {
    171. Set keys = new LinkedHashSet<>();
    172. RedisUtil.handler()
    173. .execute(
    174. (RedisConnection connection) -> {
    175. try (Cursor<byte[]> cursor =
    176. connection.scan(
    177. ScanOptions.scanOptions()
    178. .count(Long.MAX_VALUE)
    179. .match(pattern)
    180. .build())) {
    181. cursor.forEachRemaining(
    182. item -> {
    183. keys.add(RedisSerializer.string().deserialize(item));
    184. });
    185. return null;
    186. } catch (Exception e) {
    187. throw new RuntimeException(e);
    188. }
    189. });
    190. return keys;
    191. }
    192. /**
    193. * 获取key的值
    194. *
    195. * @param key 键
    196. * @return Object
    197. * @author fzr
    198. */
    199. public static Object get(String key) {
    200. key = redisPrefix + key;
    201. return redisTemplate.opsForValue().get(key);
    202. }
    203. /**
    204. * 获取旧值并设置新值
    205. *
    206. * @param key 键
    207. * @param newVal 新值
    208. * @return Object
    209. * @author fzr
    210. */
    211. public static Object getSet(String key, Object newVal) {
    212. key = redisPrefix + key;
    213. return redisTemplate.opsForValue().getAndSet(key, newVal);
    214. }
    215. /**
    216. * 设置键值对
    217. *
    218. * @param key 键
    219. * @param value 值
    220. * @author fzr
    221. */
    222. public static void set(String key, Object value) {
    223. key = redisPrefix + key;
    224. redisTemplate.opsForValue().set(key, value);
    225. }
    226. /**
    227. * 设置键值对并设置时间
    228. *
    229. * @param key 键
    230. * @param value 值
    231. * @param time time要大于0 如果time小于等于0 将设置无限期
    232. * @author fzr
    233. */
    234. public static void set(String key, Object value, long time) {
    235. key = redisPrefix + key;
    236. if (time > 0) {
    237. redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
    238. } else {
    239. set(key, value);
    240. }
    241. }
    242. /**
    243. * 递增
    244. *
    245. * @param key 键
    246. * @param delta 要增加几(大于0)
    247. * @return Long
    248. * @author fzr
    249. */
    250. public static Long incr(String key, long delta) {
    251. if (delta < 0) {
    252. throw new RuntimeException("递增因子必须大于0");
    253. }
    254. key = redisPrefix + key;
    255. return redisTemplate.opsForValue().increment(key, delta);
    256. }
    257. /**
    258. * 递减
    259. *
    260. * @param key 键
    261. * @param delta 要减少几(小于0)
    262. * @return Long
    263. * @author fzr
    264. */
    265. public static Long decr(String key, long delta) {
    266. if (delta < 0) {
    267. throw new RuntimeException("递减因子必须大于0");
    268. }
    269. key = redisPrefix + key;
    270. return redisTemplate.opsForValue().increment(key, -delta);
    271. }
    272. /* ***************** String end *************** */
    273. /**
    274. * 获取key中field域的值
    275. *
    276. * @param key 键 不能为null
    277. * @param field 项 不能为null
    278. * @return
    279. * @author fzr
    280. */
    281. public static Object hGet(String key, String field) {
    282. key = redisPrefix + key;
    283. return redisTemplate.opsForHash().get(key, field);
    284. }
    285. /**
    286. * 判断key中有没有field域名
    287. *
    288. * @param key 键
    289. * @param field 字段
    290. * @return Boolean
    291. * @author fzr
    292. */
    293. public static Boolean hExists(String key, Object field) {
    294. key = redisPrefix + key;
    295. return redisTemplate.opsForHash().hasKey(key, field);
    296. }
    297. /**
    298. * 获取hashKey对应的所有键值
    299. *
    300. * @param key 键
    301. * @return 对应的多个键值
    302. * @author fzr
    303. */
    304. public Map hmGet(String key) {
    305. key = redisPrefix + key;
    306. return redisTemplate.opsForHash().entries(key);
    307. }
    308. /**
    309. * 设置field1->N个域,对应的值是value1->N
    310. *
    311. * @param key 键
    312. * @param map 对应多个键值
    313. * @author fzr
    314. */
    315. public static void hmSet(String key, Map map) {
    316. key = redisPrefix + key;
    317. redisTemplate.opsForHash().putAll(key, map);
    318. }
    319. /**
    320. * HashSet 并设置时间
    321. *
    322. * @param key 键
    323. * @param map 对应多个键值
    324. * @param time 时间(秒)
    325. * @author fzr
    326. */
    327. public static void hmSet(String key, Map map, long time) {
    328. key = redisPrefix + key;
    329. redisTemplate.opsForHash().putAll(key, map);
    330. if (time > 0) {
    331. expire(key, time);
    332. }
    333. }
    334. /**
    335. * 向一张hash表中放入数据,如果不存在将创建
    336. *
    337. * @param key 键
    338. * @param item 项
    339. * @param value 值
    340. * @author fzr
    341. */
    342. public static void hSet(String key, String item, Object value) {
    343. key = redisPrefix + key;
    344. redisTemplate.opsForHash().put(key, item, value);
    345. }
    346. /**
    347. * 向一张hash表中放入数据,如果不存在将创建
    348. *
    349. * @param key 键
    350. * @param item 项
    351. * @param value 值
    352. * @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
    353. * @return true 成功 false失败
    354. * @author fzr
    355. */
    356. public static boolean hSet(String key, String item, Object value, long time) {
    357. key = redisPrefix + key;
    358. redisTemplate.opsForHash().put(key, item, value);
    359. if (time > 0) {
    360. expire(key, time);
    361. }
    362. return true;
    363. }
    364. /**
    365. * 删除hash表中的值
    366. *
    367. * @param key 键 不能为null
    368. * @param item 项 可以使多个 不能为null
    369. * @author fzr
    370. */
    371. public static void hDel(String key, Object... item) {
    372. key = redisPrefix + key;
    373. redisTemplate.opsForHash().delete(key, item);
    374. }
    375. /**
    376. * 判断hash表中是否有该项的值
    377. *
    378. * @param key 键 不能为null
    379. * @param item 项 不能为null
    380. * @return true 存在 false不存在
    381. * @author fzr
    382. */
    383. public static boolean hHasKey(String key, String item) {
    384. key = redisPrefix + key;
    385. return redisTemplate.opsForHash().hasKey(key, item);
    386. }
    387. /**
    388. * hash递增 如果不存在,就会创建一个并把新增后的值返回
    389. *
    390. * @param key 键
    391. * @param item 项
    392. * @param by 要增加几(大于0)
    393. * @return double
    394. * @author fzr
    395. */
    396. public static double hIncr(String key, String item, long by) {
    397. key = redisPrefix + key;
    398. return redisTemplate.opsForHash().increment(key, item, by);
    399. }
    400. /**
    401. * hash递减
    402. *
    403. * @param key 键
    404. * @param item 项
    405. * @param by 要减少记(小于0)
    406. * @return double
    407. * @author fzr
    408. */
    409. public static double hDecr(String key, String item, long by) {
    410. key = redisPrefix + key;
    411. return redisTemplate.opsForHash().increment(key, item, -by);
    412. }
    413. /* ***************** Map end *************** */
    414. /**
    415. * 根据key获取Set中的所有值
    416. *
    417. * @param key 键
    418. * @return Set
    419. * @author fzr
    420. */
    421. public static Set sGet(String key) {
    422. key = redisPrefix + key;
    423. return redisTemplate.opsForSet().members(key);
    424. }
    425. /**
    426. * 根据value从一个set中查询,是否存在
    427. *
    428. * @param key 键
    429. * @param value 值
    430. * @return true 存在 false不存在
    431. * @author fzr
    432. */
    433. public Boolean sHasKey(String key, Object value) {
    434. key = redisPrefix + key;
    435. return redisTemplate.opsForSet().isMember(key, value);
    436. }
    437. /**
    438. * 将数据放入set缓存
    439. *
    440. * @param key 键
    441. * @param values 值 可以是多个
    442. * @return 成功个数
    443. * @author fzr
    444. */
    445. public static Long sSet(String key, Object... values) {
    446. key = redisPrefix + key;
    447. return redisTemplate.opsForSet().add(key, values);
    448. }
    449. /**
    450. * 将set数据放入缓存
    451. *
    452. * @param key 键
    453. * @param time 时间(秒)
    454. * @param values 值 可以是多个
    455. * @return 成功个数
    456. * @author fzr
    457. */
    458. public Long sSetAndTime(String key, long time, Object... values) {
    459. key = redisPrefix + key;
    460. return redisTemplate.opsForSet().add(key, values);
    461. }
    462. /**
    463. * 获取set缓存的长度
    464. *
    465. * @param key 键
    466. * @return Long
    467. * @author fzr
    468. */
    469. public Long sGetSetSize(String key) {
    470. key = redisPrefix + key;
    471. return redisTemplate.opsForSet().size(key);
    472. }
    473. /**
    474. * 移除值为value的
    475. *
    476. * @param key 键
    477. * @param values 值 可以是多个
    478. * @return 移除的个数
    479. * @author fzr
    480. */
    481. public Long setRemove(String key, Object... values) {
    482. key = redisPrefix + key;
    483. return redisTemplate.opsForSet().remove(key, values);
    484. }
    485. /* ***************** Set end *************** */
    486. /**
    487. * 获取list缓存的内容
    488. *
    489. * @param key 键
    490. * @param start 开始
    491. * @param end 结束 0 到 -1代表所有值
    492. * @return List
    493. * @author fzr
    494. */
    495. public List lGet(String key, long start, long end) {
    496. key = redisPrefix + key;
    497. return redisTemplate.opsForList().range(key, start, end);
    498. }
    499. /**
    500. * 获取list缓存的长度
    501. *
    502. * @param key 键
    503. * @return Long
    504. * @author fzr
    505. */
    506. public Long lGetListSize(String key) {
    507. key = redisPrefix + key;
    508. return redisTemplate.opsForList().size(key);
    509. }
    510. /**
    511. * 通过索引获取list中的值
    512. *
    513. * @param key 键
    514. * @param index 索引 index>=0时,0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
    515. * @return Object
    516. * @author fzr
    517. */
    518. public Object lGetIndex(String key, long index) {
    519. key = redisPrefix + key;
    520. return redisTemplate.opsForList().index(key, index);
    521. }
    522. /**
    523. * 将list放入缓存
    524. *
    525. * @param key 键
    526. * @param value 值
    527. * @return boolean
    528. * @author fzr
    529. */
    530. public boolean lSet(String key, Object value) {
    531. key = redisPrefix + key;
    532. redisTemplate.opsForList().rightPush(key, value);
    533. return true;
    534. }
    535. /**
    536. * 将list放入缓存
    537. *
    538. * @param key 键
    539. * @param value 值
    540. * @param second 时间(秒)
    541. * @return boolean
    542. * @author fzr
    543. */
    544. public boolean lSet(String key, Object value, long second) {
    545. key = redisPrefix + key;
    546. redisTemplate.opsForList().rightPush(key, value);
    547. if (second > 0) expire(key, second);
    548. return true;
    549. }
    550. /**
    551. * 将list放入缓存
    552. *
    553. * @param key 键
    554. * @param value 值
    555. * @return boolean
    556. * @author fzr
    557. */
    558. public boolean lSet(String key, List value) {
    559. key = redisPrefix + key;
    560. redisTemplate.opsForList().rightPushAll(key, value);
    561. return true;
    562. }
    563. /**
    564. * 将list放入缓存
    565. *
    566. * @param key 键
    567. * @param value 值
    568. * @param time 时间(秒)
    569. * @return boolean
    570. * @author fzr
    571. */
    572. public boolean lSet(String key, List value, Long time) {
    573. key = redisPrefix + key;
    574. redisTemplate.opsForList().rightPushAll(key, value);
    575. if (time > 0) expire(key, time);
    576. return true;
    577. }
    578. /**
    579. * 根据索引修改list中的某条数据
    580. *
    581. * @param key 键
    582. * @param index 索引
    583. * @param value 值
    584. * @return boolean
    585. * @author fzr
    586. */
    587. public boolean lUpdateIndex(String key, Long index, Object value) {
    588. key = redisPrefix + key;
    589. redisTemplate.opsForList().set(key, index, value);
    590. return true;
    591. }
    592. /**
    593. * 移除N个值为value
    594. *
    595. * @param key 键
    596. * @param count 移除多少个
    597. * @param value 值
    598. * @return 移除的个数
    599. * @author fzr
    600. */
    601. public Long lRemove(String key, Long count, Object value) {
    602. key = redisPrefix + key;
    603. return redisTemplate.opsForList().remove(key, count, value);
    604. }
    605. }
    606. KaptchaConfig:用于配置 Kaptcha 验证码生成器的属性,给DefaultKaptcha进行配置属性,可以说是给图片验证码增加属性。

      1. /**
      2. * 图片验证码的配置类
      3. * */
      4. @Configuration
      5. public class KaptchaConfig {
      6. @Bean(name = "captchaProducer")
      7. public DefaultKaptcha getKaptchaBean() {
      8. DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
      9. Properties properties = new Properties();
      10. // 是否边框
      11. properties.setProperty(KAPTCHA_BORDER, "no");
      12. // 字符颜色
      13. properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "red");
      14. // 干扰线颜色
      15. properties.setProperty(KAPTCHA_NOISE_COLOR, "red");
      16. // 字符间距
      17. properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_SPACE, "5");
      18. // 图片宽度
      19. properties.setProperty(KAPTCHA_IMAGE_WIDTH, "150");
      20. // 图片高度
      21. properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "50");
      22. // 字符大小
      23. properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "40");
      24. // 字符长度
      25. properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "4");
      26. // 字体样式
      27. properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier");
      28. /// 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpy
      29. properties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.FishEyeGimpy");
      30. defaultKaptcha.setConfig(new Config(properties));
      31. return defaultKaptcha;
      32. }
      33. }

      如果你想要设置图片验证码的相关属性可以对properties进行对应的设置和修改即可。

    607. 相关阅读:
      [事务]-事务概念/特性/并发问题/传播特性
      【openstack】cloudkitty组件,入门级安装(快速)
      计算机网络 HTTPS
      音视频开发项目:H.265播放器:视频解码篇
      SpringBoot保姆级教程(八)热部署 & 整合MyBatis
      大整数加法
      新加坡大带宽服务器托管优势
      视频 | 生信分析Linux教程 - Linux系统简介和目录理解2
      【Linux】VM及WindowsServer安装
      python之lambda表达式
    608. 原文地址:https://blog.csdn.net/m0_64210833/article/details/133904859