{"record":{"id":"a42f10f70d7bce37","repo":"wuyouzhuguli/SpringAll","slug":"deviceid","errorCode":null,"errorMessage":"请在请求头中设置deviceId","messagePattern":"请在请求头中设置deviceId","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"64.Spring-Security-OAuth2-Customize/src/main/java/cc/mrbird/security/service/RedisCodeService.java","lineNumber":56,"sourceCode":"     * @return 验证码\n     */\n    public String get(ServletWebRequest request, String mobile) throws Exception {\n        return redisTemplate.opsForValue().get(key(request, mobile));\n    }\n\n    /**\n     * 移除验证码\n     *\n     * @param request ServletWebRequest\n     */\n    public void remove(ServletWebRequest request, String mobile) throws Exception {\n        redisTemplate.delete(key(request, mobile));\n    }\n\n    private String key(ServletWebRequest request, String mobile) throws Exception {\n        String deviceId = request.getHeader(\"deviceId\");\n        if (StringUtils.isBlank(deviceId)) {\n            throw new Exception(\"请在请求头中设置deviceId\");\n        }\n        return SMS_CODE_PREFIX + deviceId + \":\" + mobile;\n    }\n}\n","sourceCodeStart":38,"sourceCodeEnd":61,"githubUrl":"https://github.com/wuyouzhuguli/SpringAll/blob/614d2578d9495acf53cc02f2dee9c6131cc5e51a/64.Spring-Security-OAuth2-Customize/src/main/java/cc/mrbird/security/service/RedisCodeService.java#L38-L61","documentation":"Generic java.lang.Exception thrown by RedisCodeService.key when the 'deviceId' request header is blank. The Redis key for an SMS code is 'SMS_CODE:' + deviceId + ':' + mobile, so deviceId is mandatory to namespace codes per device. This is the project-64 Redis-backed variant of SMS code storage (replaces the session-based approach).","triggerScenarios":"Any call to RedisCodeService.save/get/remove (i.e. /code/sms generation or /login/mobile validation) without a 'deviceId' request header, or with an empty/whitespace value.","commonSituations":"Frontend/Postman omitted the deviceId header for the SMS endpoints; mobile app not sending a device identifier; header name casing/typo (Device-Id vs deviceId).","solutions":["Send a non-empty 'deviceId' request header on both GET /code/sms and POST /login/mobile (and any route through SmsCodeFilter).","On mobile, use a stable per-install identifier (IDFV/Android ID/UUID persisted on first launch) as deviceId.","Header names are case-insensitive per HTTP, but the value must be non-blank.","Consider throwing a domain-specific exception instead of generic Exception for cleaner handling."],"exampleFix":"// before\n// fetch('/code/sms?mobile=13800000000')\n\n// after\nfetch('/code/sms?mobile=13800000000', {\n  headers: { 'deviceId': deviceUuid }\n});","handlingStrategy":"validation","validationCode":"// Persist a stable deviceId and attach it to every SMS-related request.\nlet deviceId = localStorage.getItem('deviceId');\nif (!deviceId) { deviceId = crypto.randomUUID(); localStorage.setItem('deviceId', deviceId); }\nfunction smsHeaders() { return { deviceId }; }\n// use on GET /code/sms and POST /login/mobile","typeGuard":null,"tryCatchPattern":"try { await sendSms(mobile); }\ncatch (e) {\n  if (/deviceId/.test(e.message)) { ensureDeviceIdHeader(); retry(); }\n  else handleError(e);\n}","preventionTips":["Generate and persist deviceId on first app launch; reuse it.","Attach deviceId on both /code/sms and /login/mobile.","Consider a domain exception type instead of generic Exception on the server."],"tags":["sms","redis","configuration","http-headers","spring-security"],"backgroundTag":null,"analyzedSha":"614d2578d9495acf53cc02f2dee9c6131cc5e51a","analyzedAt":"2026-08-14T04:40:03.488Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}