spring boot和redis實現自定義前後分離token認證

說明:文章部分代碼引用自github

本項目地址:https://gitee.com/indexman/redis-token-demo

1.token認證流程

此處以前端頁面請求後端用戶列表接口爲例:
在這裏插入圖片描述

2.用到的技術

  • redis:存儲用戶及token信息
  • localstorage:前端存儲獲取到的token
  • 自定義攔截器:用於攔截和校驗HTTP請求中token的有效性

3.實現效果展示

  • 登錄獲取token

在這裏插入圖片描述

  • 查看redis中token信息

在這裏插入圖片描述

  • token失效後跳轉到登錄頁

在這裏插入圖片描述

4.核心源碼展示

4.1 token鑑權接口

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface AuthToken {

}

4.1 自定義攔截器

@Slf4j
public class AuthorizationInterceptor implements HandlerInterceptor {


    //存放鑑權信息的Header名稱,默認是Authorization
    private String httpHeaderName = "Authorization";

    //鑑權失敗後返回的錯誤信息,默認爲401 unauthorized
    private String unauthorizedErrorMessage = "401 unauthorized";

    //鑑權失敗後返回的HTTP錯誤碼,默認爲401
    private int unauthorizedErrorCode = HttpServletResponse.SC_UNAUTHORIZED;

    /**
     * 存放登錄用戶模型Key的Request Key
     */
    public static final String REQUEST_CURRENT_KEY = "REQUEST_CURRENT_KEY";


    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method        method        = handlerMethod.getMethod();
        // 如果打上了AuthToken註解則需要驗證token
        if (method.getAnnotation(AuthToken.class) != null || handlerMethod.getBeanType().getAnnotation(AuthToken.class) != null) {


            String token = request.getHeader(httpHeaderName);
            log.info("token is {}", token);
            String username = "";
            Jedis  jedis    = new Jedis("localhost", 6379);
            if (token != null && token.length() != 0) {
                username = jedis.get(token);
                log.info("username is {}", username);
            }
            if (username != null && !username.trim().equals("")) {
                //log.info("token birth time is: {}",jedis.get(username+token));
                Long tokeBirthTime = Long.valueOf(jedis.get(token + username));
                log.info("token Birth time is: {}", tokeBirthTime);
                Long diff = System.currentTimeMillis() - tokeBirthTime;
                log.info("token is exist : {} ms", diff);
                if (diff > ConstantKit.TOKEN_RESET_TIME) {
                    jedis.expire(username, ConstantKit.TOKEN_EXPIRE_TIME);
                    jedis.expire(token, ConstantKit.TOKEN_EXPIRE_TIME);
                    log.info("Reset expire time success!");
                    Long newBirthTime = System.currentTimeMillis();
                    jedis.set(token + username, newBirthTime.toString());
                }

                //用完關閉
                jedis.close();
                request.setAttribute(REQUEST_CURRENT_KEY, username);
                return true;


            } else {
                JSONObject jsonObject = new JSONObject();

                PrintWriter out = null;
                try {
                    response.setStatus(unauthorizedErrorCode);
                    response.setContentType(MediaType.APPLICATION_JSON_VALUE);

                    jsonObject.put("code", ((HttpServletResponse) response).getStatus());
                    jsonObject.put("message", HttpStatus.UNAUTHORIZED);
                    out = response.getWriter();
                    out.println(jsonObject);

                    return false;
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    if (null != out) {
                        out.flush();
                        out.close();
                    }
                }

            }

        }

        request.setAttribute(REQUEST_CURRENT_KEY, null);

        return true;
    }

4.3 用戶controller

@RestController
@Slf4j
@RequestMapping("/api/user")
public class UserController {

    @Autowired
    Md5TokenGenerator tokenGenerator;

    @Autowired
    UserMapper userMapper;

    @RequestMapping(value = "login", method = RequestMethod.POST)
    @ApiOperation("用戶登錄接口")
    public ResponseTemplate login(@RequestBody(required = false) JSONObject userInfo) {

        String username = userInfo.getString("username");
        String password = userInfo.getString("password");

        List<User> users = userMapper.selectList(new EntityWrapper<User>()
                .eq("username", username)
                .eq("password", password));
        JSONObject result = new JSONObject();

        if(users.size()>0){
            User currentUser = users.get(0);
            if(currentUser!=null){
                Jedis  jedis = new Jedis("localhost", 6379);
                String token = tokenGenerator.generate(username, password);
                jedis.set(username, token);
                jedis.expire(username, ConstantKit.TOKEN_EXPIRE_TIME);
                jedis.set(token, username);
                jedis.expire(token, ConstantKit.TOKEN_EXPIRE_TIME);
                Long currentTime = System.currentTimeMillis();
                jedis.set(token + username, currentTime.toString());

                //用完關閉
                jedis.close();

                result.put("code",200);
                result.put("status", "登錄成功");
                result.put("token", token);
            }
        }else{
            result.put("code",400);
            result.put("status", "登錄失敗");
            result.put("token", "");
        }


        return ResponseTemplate.builder()
                .code(result.getInteger("code"))
                .message(result.getString("status"))
                .data(result.getString("token"))
                .build();

    }


    @ApiOperation("查詢用戶列表")
    @RequestMapping(value = "listAll", method = RequestMethod.GET)
    @AuthToken
    public ResponseTemplate listAll() {
        List<User> user = new User().selectAll();
        return ResponseTemplate.builder()
                .code(200)
                .message("Success")
                .data(user)
                .build();
    }
}

4.4 登錄頁

$(function() {
        $("#login-btn").click(function (event) {
            // 阻止表單默認提交
            event.preventDefault();

            var username = $("#username").val(), password = $("#password").val();
            if(username==""){
                alert("用戶名不能爲空!");
                return;
            }
            if(password==""){
                alert("密碼不能爲空!");
                return;
            }

            var param={"username": username, "password": password}

            // 提交驗證
            $.ajax({
                type: "POST",
                url: "/api/user/login",
                contentType: "application/json",
                data: JSON.stringify(param),
                success: function (result) {
                    console.log(result)
                    if(result.code==200){
                        window.localStorage.setItem("token",result.data)
                        window.location.href="users.html";
                    }else{
                        alert(result);
                    }
                }
            });


        });
    });

4.5 用戶列表頁

$(function() {
            var token = window.localStorage.getItem("token")
            console.log(token)
            // 提交驗證
            $.ajax({
                type: "GET",
                url: "/api/user/listAll",
                headers:{'Authorization':token},
                contentType: "application/json",
                success: function (result) {
                    console.log(result)
                    if(result.code==200){
                        if (result.data != null && result.data!='') {
                            // 拼接列表
                            var dataRow = '<tr><td>ID</td><td>用戶名</td><td>密碼</td></tr>';
                            $.each(result.data, function (i, r) {
                                dataRow += '<tr>'
                                    + '<td>'
                                    + r.id
                                    + '</td>'
                                    + '<td>'
                                    + r.username
                                    + '</td><td>'
                                    + r.password + '</td>'
                                ;

                                dataRow += '</tr>';
                            });

                            // console.log(dataRow);
                            $("#tb-users").empty();
                            $("#tb-users").append(dataRow);
                        }
                    }else{
                        alert(result);
                    }
                },
                error:function(result){
                    if(result.status==401){
                        window.location.href="login.html";
                    }
                }
            });
        });
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章