zoukankan      html  css  js  c++  java
  • 【实战】Springboot +jjwt+注解实现需登录才能操作

    springboot +jjwt+注解实现需登录才能调用接口

    1.开发需要登录才能进行操作的自定义注解NeedLogin,后面可以写在需要登陆后操作的接口上

    package com.songzhen.howcool.auth;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Target({ElementType.METHOD, ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface NeedLogin {
        boolean required() default true;
    }
    View Code

    2.开发拦截器(Interceptor),预处理全部请求

    package com.songzhen.howcool.auth;
    
    import com.alibaba.fastjson.JSON;
    import com.songzhen.howcool.model.enums.RetCodeEnum;
    import com.songzhen.howcool.util.JwtUtil;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.web.method.HandlerMethod;
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.lang.reflect.Method;
    import java.util.HashMap;
    import java.util.Map;
    
    public class AuthenticationInterceptor implements HandlerInterceptor {
    
        @Autowired
        private RedisTemplate<String, String> redisTemplate;
    
        /**
         * 预处理回调方法,实现处理器的预处理(如检查登陆),第三个参数为响应的处理器,自定义Controller
         * 返回值:true表示继续流程(如调用下一个拦截器或处理器);false表示流程中断(如登录检查失败),不会继续调用其他的拦截器或处理器,此时我们需要通过response来产生响应;
         */
        @Override
        public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception {
    
            // 判断对象是否是映射到一个方法,如果不是则直接通过
            if (!(object instanceof HandlerMethod)) {
                // instanceof运算符是用来在运行时指出对象是否是特定类的一个实例
                return true;
            }
            HandlerMethod handlerMethod = (HandlerMethod) object;
            Method method = handlerMethod.getMethod();
            //检查方法是否有NeedLogin注解,无则跳过认证
            if (method.isAnnotationPresent(NeedLogin.class)) {
                NeedLogin needLogin = method.getAnnotation(NeedLogin.class);
                if (needLogin.required()) {
                    // 从HTTP请求头中获取TOKEN信息
                    String token = httpServletRequest.getHeader("Authorization");
    
                    // HTTP请求头中TOKEN解析出的用户信息
                    String uid = JwtUtil.getUid(token);
                    String userName = JwtUtil.getUserName(token);
                    String realName = JwtUtil.getRealName(token);
    
                    // 检查TOKEN
                    if (!checkToken(token)) {
                        // TOKEN错误时,提示用户登录
                        Map<String, Object> retMap = new HashMap<>(16);
                        retMap.put("code", RetCodeEnum.ACCOUNT_UNAUTHORIZED.getCode());
                        retMap.put("msg", RetCodeEnum.ACCOUNT_UNAUTHORIZED.getDesc());
                        httpServletResponse.setContentType("application/json;charset=UTF-8");
                        httpServletResponse.getWriter().append(JSON.toJSONString(retMap));
                        return false;
                    }
    
                    // 组装用户信息到REQUEST中
                    Map<String, Object> currentUser = new HashMap<>(16);
                    currentUser.put("uid", uid);
                    currentUser.put("userName", userName);
                    currentUser.put("realName", realName);
                    httpServletRequest.setAttribute("currentUser", currentUser);
    
                    return true;
                }
            }
            return true;
        }
    
        /**
         * 后处理回调方法,实现处理器的后处理(但在渲染视图之前),此时我们可以通过modelAndView(模型和视图对象)对模型数据进行处理或对视图进行处理,modelAndView也可能为null。
         */
        @Override
        public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object, ModelAndView modelAndView) throws Exception {
            long now = System.currentTimeMillis();
        }
    
        /**
         * 整个请求处理完毕回调方法,即在视图渲染完毕时回调,如性能监控中我们可以在此记录结束时间并输出消耗时间,还可以进行一些资源清理,类似于try-catch-finally中的finally,但仅调用处理器执行链中
         */
        @Override
        public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
        }
    
        /**
         * 检查TOKEN
         *
         * @param token token
         *              要校验的token
         * @return boolean
         * true:通过 false:失败
         */
        private boolean checkToken(String token) {
            // ------------------------认证------------开始-----------------
            if (null == token) {
                return false;
            }
    
            // 获取TOKEN中的用户信息
            String uid = JwtUtil.getUid(token);
    
            // 根据uid从redis中获取用户tokenInRedis
            String tokenInRedis = redisTemplate.opsForValue().get(uid);
            if (null == tokenInRedis) {
                // 如果REDIS异常,返回成功保证正常业务可以继续处理
                return true;
            }
    
            // HTTP请求头中TOKEN解析出的用户信息
            String userName = JwtUtil.getUserName(token);
            String realName = JwtUtil.getRealName(token);
            String deviceId = JwtUtil.getDeviceId(token);
            long expireIn = JwtUtil.getExpireIn(token);
            // REDIS服务器中TOKEN解析出的用户信息
            String userNameInRedis = JwtUtil.getUserName(tokenInRedis);
            String realNameInRedis = JwtUtil.getRealName(tokenInRedis);
            String deviceIdInRedis = JwtUtil.getDeviceId(tokenInRedis);
            long expireInInRedis = JwtUtil.getExpireIn(tokenInRedis);
    
            if (null == userName || null == realName || null == deviceId) {
                return false;
            }
            if (null == userNameInRedis || null == realNameInRedis || null == deviceIdInRedis) {
                return false;
            }
            // 判断TOKEN是否过期
            if (expireIn != expireInInRedis) {
                return false;
            }
            if (expireIn < System.currentTimeMillis()) {
                return false;
            }
            // ------------------------认证------------结束-----------------
            return true;
        }
    
    }
    View Code

    3.开发拦截器配置类

    package com.songzhen.howcool.config;
    
    import com.songzhen.howcool.auth.AuthenticationInterceptor;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    
    @Configuration
    public class InterceptorConfig implements WebMvcConfigurer {
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(authenticationInterceptor())
                    .addPathPatterns("/**");
        }
        @Bean
        public AuthenticationInterceptor authenticationInterceptor() {
            return new AuthenticationInterceptor();
        }
    }
    View Code

    4.在Controller中需要登录才能操作的方法上加上注解NeedLogin

    package com.songzhen.howcool.controller;
    
    import com.songzhen.howcool.entity.QueryUserEntity;
    import com.songzhen.howcool.entity.UserLoginEntity;
    import com.songzhen.howcool.auth.NeedLogin;
    import com.songzhen.howcool.biz.UserBizService;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import javax.servlet.http.HttpServletRequest;
    import java.util.Map;
    
    /**
     * 用户相关.
     * <p>用户相关<br>
     * 用户相关
     *
     * @author Lucas
     * @date 2018/8/9
     */
    @RestController
    @RequestMapping("**/v1/user")
    public class UserController {
    
        private static final Logger logger = LoggerFactory.getLogger(UserController.class);
    
        @Autowired
        private UserBizService userBizService;
    
        /**
         * 登录
         */
        @PostMapping("login")
        public Map<String, Object> login(@RequestBody UserLoginEntity userLoginEntity) {
            logger.info("login input params userLoginEntity={}", userLoginEntity);
            return userBizService.login(userLoginEntity.getUserName(), userLoginEntity.getPassword(), userLoginEntity.getDeviceId());
        }
    
    
        /**
         * 登出
         *
         * @param request request
         */
        @NeedLogin
        @PostMapping("logout")
        public Map<String, Object> logout(HttpServletRequest request) {
            Map<String,Object> currentUser = (Map<String,Object>)request.getAttribute("currentUser");
    
            return userBizService.logout(currentUser.get("uid").toString());
        }
    
        /**
         * 列表用户
         */
        @NeedLogin
        @PostMapping("pageUser")
        public Map<String, Object> pageUser(@RequestBody QueryUserEntity queryUserEntity) {
            logger.info("login input params queryUserEntity={}", queryUserEntity);
            return userBizService.pageUsers(queryUserEntity);
        }
    
    }
    View Code

    5.GitHub上源代码地址

    https://github.com/songzhen110/howcool.git

  • 相关阅读:
    linux 文件权限(s、t、i、a)解析
    vim Vundle
    数据结构学习(1)
    Android ImageView设置图片原理(上)
    C++11 之auto
    Android屏幕分辨率获取方法--源码剖析
    C++的发展方向是对的嘛?
    c++ 的前世今生
    学习知识的一种思路
    遗失的访谈小评
  • 原文地址:https://www.cnblogs.com/lucas1024/p/10582314.html
Copyright © 2011-2022 走看看