jwt+redis+mybatis+security整合

Security+Jwt+Redis+Mybatis组合配置

Springboot 配置 Redis

  • pom 引入spring-boot-starter-data-redis 包

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    
  • propertites配置文件,配置redis信息

    # Redis数据库索引(默认为0)
    spring.redis.database=0
    # Redis服务器地址
    spring.redis.host=127.0.0.1
    # Redis服务器连接端口
    spring.redis.port=6379
    # Redis服务器连接密码(默认为空)
    spring.redis.password=root
    # 连接池最大连接数(使用负值表示没有限制)
    spring.redis.jedis.pool.max-active=20
    # 连接池最大阻塞等待时间(使用负值表示没有限制)
    spring.redis.jedis.pool.max-wait=-1
    # 连接池中的最大空闲连接
    spring.redis.jedis.pool.max-idle=10
    # 连接池中的最小空闲连接
    spring.redis.jedis.pool.min-idle=0
    # 连接超时时间(毫秒)
    spring.redis.timeout=1000
    
  • 设置redis的配置文件

    /**
     * @Notes redis 配置
     * @Date 2022/3/21
     * @Time 12:21
     * @Author smile
     */
    @Configuration
    public class RedisConfig{
    
    	@Bean
        public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory factory)
        {
            RedisTemplate<String,Object> template = new RedisTemplate<>();
            template.setConnectionFactory(factory);
    
            Jackson2JsonRedisSerializer<Object> jsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(Object.class);
    
            //redis的key
            template.setValueSerializer(jsonRedisSerializer);
            template.setKeySerializer(new StringRedisSerializer());
    
            //hash的key
            template.setHashValueSerializer(jsonRedisSerializer);
            template.setHashKeySerializer(new StringRedisSerializer());
    
            return template;
        }
    }
    
  • 测试案例

    @Autowired
    private RedisTemplate redisTemplate;
    
    @Test
    public void TestRedis() {
        redisTemplate.opsForValue().set("smile","易文杰");
    
        System.out.println(redisTemplate.opsForValue().get("smile"));
    }
    

    image-20220321144158704.png

Springboot 配置 Jwt

  • pom 引入jwt依赖

    <dependency>
        <groupId>com.auth0</groupId>
        <artifactId>java-jwt</artifactId>
        <version>3.18.3</version>
    </dependency>
    
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.33</version>
    </dependency>
    
  • 设置jwt工具类

    package com.smile.blog.utils.jwt;
    
    import com.auth0.jwt.JWT;
    import com.auth0.jwt.JWTCreator;
    import com.auth0.jwt.algorithms.Algorithm;
    import com.auth0.jwt.interfaces.DecodedJWT;
    
    import java.util.Calendar;
    import java.util.Map;
    import java.util.Objects;
    
    /**
     * @Notes
     * @Date 2022/3/18
     * @Time 17:56
     * @Author smile
     */
    public class Jwt {
        /***
         * 过期时间秒 默认过期时间
         **/
        public static final int EXPIRE = 60 * 24 * 60 * 60;
    
        /***
         * 加密明文
         **/
        public static final String KEY = "6TvUubi2AD7wMFFfeN84pTjcxngz8sqraCLliknFBcn32y99cA1q7WDomJmX1BMF";
    
        /***
         * @Notes  获取到token值
         *
         * @return java.lang.String
         * @author smile
         * @date 2022/3/21
         * @time 14:58
         **/
        public static String token(Map<String,String> payload)
        {
            Calendar calendar = Calendar.getInstance();
            calendar.add(Calendar.SECOND,EXPIRE);
    
            JWTCreator.Builder builder = JWT.create();
    
            if (!Objects.isNull(payload)) {
                payload.forEach(builder::withClaim);
            }
    
            return builder.withExpiresAt(calendar.getTime())
                    .sign(Algorithm.HMAC256(KEY));
        }
    
        /***
         * @Notes 验证token值
         * @param token token值
         * @author smile
         * @date 2022/3/16
         * @time 18:21
         **/
        public static void verify(String token)
        {
            JWT.require(Algorithm.HMAC256(KEY))
                    .build()
                    .verify(token);
        }
    
        /***
         * @Notes 解析token
         * @param token token值
         * @return DecodedJWT
         * @author smile
         * @date 2022/3/16
         * @time 18:23
         **/
        public static DecodedJWT parse(String token)
        {
            return JWT.require(Algorithm.HMAC256(KEY))
                    .build()
                    .verify(token);
        }
    }
    
  • 测试案例

    Map<String,String> map = new HashMap<String,String>();
    map.put("userId","123");
    
    System.out.println(Jwt.token(map));
    

    测试结果如下所示:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2NTMwNTQzNzIsInVzZXJJZCI6IjEyMyJ9.Xb4yPxbKGqtppI64buahcqTNn5m2uXASd5_ioZcMCRQ

    Map<String,String> map = new HashMap<String,String>();
    map.put("userId","123");
    
    String res = Jwt.parse(token).getClaim("userId").toString();
    
    System.out.println(res);
    

    测试结果如下所示:

    "123"

  • 添加restfulApi响应

    • ApiResponse

      package com.smile.blog.utils.response;
      
      import com.alibaba.fastjson.annotation.JSONField;
      
      /**
       * @Notes
       * @Date 2022/3/18
       * @Time 11:31
       * @Author smile
       */
      public class ApiResponse <T>{
          /**
           * 状态码
           */
          @JSONField(name = "code")
          private long code;
      
          /**
           * 提示消息
           */
          @JSONField(name = "message")
          private String message;
      
          /**
           * 接口返回数据
           */
          @JSONField(name = "data")
          private T data;
      
          /**
           * 接口状态码
           */
          @JSONField(name = "status")
          private String status;
      
          protected ApiResponse() {
      
          }
      
          public ApiResponse(long code, String message, T data, String status) {
              this.code = code;
              this.message = message;
              this.data = data;
              this.status = status;
          }
      
      
          public static <T> ApiResponse<T> success()
          {
              return new ApiResponse<T>(ResponseCode.SUCCESS.getCode(),
                      ResponseCode.SUCCESS.getMessage(),
                      null,
                      ResponseCode.SUCCESS.getStatus());
          }
      
          public static <T> ApiResponse<T> success(String message)
          {
              return new ApiResponse<T>(ResponseCode.SUCCESS.getCode(),
                      message,
                      null,
                      ResponseCode.SUCCESS.getStatus());
          }
      
          public static <T> ApiResponse<T> success(String message,T data)
          {
              return new ApiResponse<T>(ResponseCode.SUCCESS.getCode(),
                      message,
                      data,
                      ResponseCode.SUCCESS.getStatus());
          }
      
          public static <T> ApiResponse<T> success(String message,T data,long code)
          {
              return new ApiResponse<T>(code,
                      message,
                      data,
                      ResponseCode.SUCCESS.getStatus());
          }
      
          public static <T> ApiResponse<T> failed()
          {
              return new ApiResponse<T>(ResponseCode.FAILED.getCode(),
                      ResponseCode.FAILED.getMessage(),
                      null,
                      ResponseCode.FAILED.getStatus());
          }
      
          public static <T> ApiResponse<T> failed(String message)
          {
              return new ApiResponse<T>(ResponseCode.FAILED.getCode(),
                      message,
                      null,
                      ResponseCode.FAILED.getStatus());
          }
      
          public static <T> ApiResponse<T> failed(String message,T data)
          {
              return new ApiResponse<T>(ResponseCode.FAILED.getCode(),
                      message,
                      data,
                      ResponseCode.FAILED.getStatus());
          }
      
          public static <T> ApiResponse<T> failed(String message,T data,long code)
          {
              return new ApiResponse<T>(code,
                      message,
                      data,
                      ResponseCode.FAILED.getStatus());
          }
      
          public static <T> ApiResponse<T> error(T data)
          {
              return new ApiResponse<T>(ResponseCode.ERROR.getCode(),
                      ResponseCode.ERROR.getMessage(),
                      null,
                      ResponseCode.ERROR.getStatus());
          }
      
          public static <T> ApiResponse<T> valid()
          {
              return new ApiResponse<T>(ResponseCode.VALIDATE_FAILED.getCode(),
                      ResponseCode.VALIDATE_FAILED.getMessage(),
                      null,
                      ResponseCode.VALIDATE_FAILED.getStatus());
          }
      
          public static <T> ApiResponse<T> valid(String message)
          {
              return new ApiResponse<T>(ResponseCode.VALIDATE_FAILED.getCode(),
                      message,
                      null,
                      ResponseCode.VALIDATE_FAILED.getStatus());
          }
      
          public long getCode() {
              return code;
          }
      
          public void setCode(long code) {
              this.code = code;
          }
      
          public String getMessage() {
              return message;
          }
      
          public void setMessage(String message) {
              this.message = message;
          }
      
          public T getData() {
              return data;
          }
      
          public void setData(T data) {
              this.data = data;
          }
      
          public String getStatus() {
              return status;
          }
      
          public void setStatus(String status) {
              this.status = status;
          }
      }
      
    • IErrorCode

      package com.smile.blog.utils.response;
      
      public interface IErrorCode {
          /***
           * 返回状态码
           **/
          long getCode();
      
          /***
           * 返回状态值
           **/
          String getStatus();
      
          /***
           * 返回信息
           **/
          String getMessage();
      }
      
    • ResponseCode

      package com.smile.blog.utils.response;
      
      public enum ResponseCode implements IErrorCode{
          SUCCESS(200,"成功","success"),
      
          FAILED(400,"失败","error"),
      
          UNAUTHORIZED(401,"身份验证失败","error"),
      
          VALIDATE_FAILED(402,"参数校验异常","error"),
      
          FORBIDDEN(403,"无权限访问","error"),
      
          NOT_FOUND(404,"路由未找到","error"),
      
          METHOD_NOT_ALLOWED (405,"请求方法不支持","error"),
      
          HTTP_TOO_MANY_REQUESTS(429,"接口频率限制","error"),
      
          ERROR(500,"系统异常","error");
      
          /***
           * 状态码
           **/
          private long code;
      
          /***
           * 消息
           **/
          private String message;
      
          /***
           * 状态值
           **/
          private String status;
      
          ResponseCode(long code, String message, String status) {
              this.code = code;
              this.message = message;
              this.status = status;
          }
      
          @Override
          public long getCode() {
              return code;
          }
      
          @Override
          public String getStatus() {
              return status;
          }
      
          @Override
          public String getMessage() {
              return message;
          }
      }
      
    • HttpResponse

      package com.smile.blog.utils.response;
      
      import javax.servlet.http.HttpServletResponse;
      import java.io.IOException;
      
      public class HttpResponse {
          public static void response(HttpServletResponse response, String string) throws IOException {
              response.setStatus(200);
              response.setContentType("application/json");
              response.setCharacterEncoding("utf-8");
              response.getWriter().print(string);
          }
      }
      
  • 添加jwt过滤器

    package com.smile.blog.filter;
    
    import com.alibaba.fastjson.JSON;
    import com.auth0.jwt.exceptions.AlgorithmMismatchException;
    import com.auth0.jwt.exceptions.SignatureVerificationException;
    import com.auth0.jwt.exceptions.TokenExpiredException;
    import com.smile.blog.utils.jwt.Jwt;
    import com.smile.blog.utils.response.ApiResponse;
    import com.smile.blog.utils.response.ResponseCode;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.stereotype.Component;
    import org.springframework.util.StringUtils;
    import org.springframework.web.filter.OncePerRequestFilter;
    
    import javax.annotation.Resource;
    import javax.servlet.FilterChain;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.util.Objects;
    
    /**
     * @Notes
     * jwt 过滤 验证token的有效性
     * @Date 2022/3/18
     * @Time 20:25
     * @Author smile
     */
    @Component
    public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
        @Resource
        private RedisTemplate<String,String> redisTemplate;
    
        @Override
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
            String token = request.getHeader("authorization");
    
            //判断token是否存在
            if (!StringUtils.hasText(token)) {
                filterChain.doFilter(request,response);
    
                return;
            }
    
            //验证token是否正常
            Object apiResponse = null;
    
            try {
                Jwt.verify(token);
    
                String payload = Jwt.parse(token).getClaim("userId").toString();
    
                if (payload.isEmpty()) {
                    apiResponse = ApiResponse.failed("身份验证失败",null, ResponseCode.UNAUTHORIZED.getCode());
                }
    
                Object userCache = redisTemplate.opsForHash().get("users","user_"+payload);
    
                if (Objects.isNull(userCache) || userCache.toString().isEmpty()) {
                    apiResponse = ApiResponse.failed("身份验证失败",null, ResponseCode.UNAUTHORIZED.getCode());
                }
    
                //将信息存放于SecurityContextHolder
                filterChain.doFilter(request, response);
            }catch (SignatureVerificationException exception){
                exception.printStackTrace();
                apiResponse = ApiResponse.failed("无效签名",null, ResponseCode.UNAUTHORIZED.getCode());
            } catch (TokenExpiredException exception){
                exception.printStackTrace();
                apiResponse = ApiResponse.failed("token过期",null, ResponseCode.UNAUTHORIZED.getCode());
            } catch (AlgorithmMismatchException exception){
                exception.printStackTrace();
                apiResponse = ApiResponse.failed("token签名算法不一致",null, ResponseCode.UNAUTHORIZED.getCode());;
            } catch (Exception exception){
                exception.printStackTrace();
                apiResponse = ApiResponse.failed("token无效",null, ResponseCode.UNAUTHORIZED.getCode());
            }
    
            if (Objects.isNull(apiResponse)) {
                filterChain.doFilter(request,response);
            } else {
                response.setStatus(200);
                response.setContentType("application/json");
                response.setCharacterEncoding("utf-8");
                response.getWriter().print(JSON.toJSONString(apiResponse));
            }
        }
    }
    
Springboot 配置
  • pom引入依赖

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    
  • 创建配置文件

    package com.smile.blog.config;
    
    import com.smile.blog.filter.JwtAuthenticationTokenFilter;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.authentication.AuthenticationManager;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    import org.springframework.security.config.http.SessionCreationPolicy;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    import org.springframework.security.crypto.password.PasswordEncoder;
    import org.springframework.security.web.AuthenticationEntryPoint;
    import org.springframework.security.web.access.AccessDeniedHandler;
    import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
    
    import javax.annotation.Resource;
    
    /**
     * @Notes
     * @Date 2022/3/17
     * @Time 16:53
     * @Author smile
     */
    @Configuration
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
        private final String[] PATTERNS = {
                "/article/details"
        };
    
        @Resource
        private AccessDeniedHandler AccessDeniedHandleImpl;
    
        @Resource
        private AuthenticationEntryPoint AuthenticationEntryPointImpl;
    
        @Resource
        private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
    
        /***
         * @Notes 定义加密规则
         *
         * @return org.springframework.security.crypto.password.PasswordEncoder
         * @author smile
         * @date 2022/3/17
         * @time 16:59
         **/
        @Bean
        public PasswordEncoder passwordEncoder()
        {
            return new BCryptPasswordEncoder();
        }
    
        @Bean
        @Override
        public AuthenticationManager authenticationManagerBean() throws Exception 
        {
            return super.authenticationManagerBean();
        }
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.csrf().disable()
                    .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                    .and()
                    .authorizeRequests()
                    .antMatchers("/auth/login").anonymous()
                    .antMatchers(PATTERNS).permitAll()
                    .anyRequest().authenticated();
    
            //配置异常处理
            http.exceptionHandling()
                    .authenticationEntryPoint(AuthenticationEntryPointImpl)
                    .accessDeniedHandler(AccessDeniedHandleImpl);
    
            //添加过滤器
            http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
    
            //跨域
            http.cors();
        }
    }
    
  • 创建认证异常实现类

    package com.smile.blog.handler;
    
    import com.alibaba.fastjson.JSON;
    import com.smile.blog.utils.http.HttpResponse;
    import com.smile.blog.utils.response.ApiResponse;
    import com.smile.blog.utils.response.ResponseCode;
    import org.springframework.security.core.AuthenticationException;
    import org.springframework.security.web.AuthenticationEntryPoint;
    import org.springframework.stereotype.Component;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    /**
     * @Notes 认证失败
     * @Date 2022/3/19
     * @Time 11:11
     * @Author smile
     */
    @Component
    public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint {
        @Override
        public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
            ApiResponse<Object> result = ApiResponse.failed(authException.getMessage(),null, ResponseCode.UNAUTHORIZED.getCode());
    
            String json = JSON.toJSONString(result);
    
            HttpResponse.response(response,json);
        }
    }
    
  • 创建权限异常实现类

    package com.smile.blog.handler;
    
    import com.alibaba.fastjson.JSON;
    import com.smile.blog.utils.http.HttpResponse;
    import com.smile.blog.utils.response.ApiResponse;
    import com.smile.blog.utils.response.ResponseCode;
    import org.springframework.security.access.AccessDeniedException;
    import org.springframework.security.web.access.AccessDeniedHandler;
    import org.springframework.stereotype.Component;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    /**
     * @Notes
     * @Date 2022/3/19
     * @Time 11:22
     * @Author smile
     */
    @Component
    public class AccessDeniedHandleImpl implements AccessDeniedHandler {
        @Override
        public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
            ApiResponse<Object> result = ApiResponse.failed("权限不足",null, ResponseCode.FORBIDDEN.getCode());
    
            String json = JSON.toJSONString(result);
    
            HttpResponse.response(response,json);
        }
    }
    
  • 创建User登录类

    package com.smile.blog.domain;
    
    import lombok.Data;
    import com.smile.blog.entity.User;
    import lombok.AllArgsConstructor;
    import lombok.NoArgsConstructor;
    import org.springframework.security.core.GrantedAuthority;
    import org.springframework.security.core.userdetails.UserDetails;
    
    import java.util.Collection;
    
    /**
     * @Notes
     * @Date 2022/3/18
     * @Time 17:29
     * @Author smile
     */
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class UserLogin implements UserDetails {
        private User user;
    
        @Override
        public Collection<? extends GrantedAuthority> getAuthorities() {
            return null;
        }
    
        @Override
        public String getPassword() {
            return user.getPassword();
        }
    
        @Override
        public String getUsername() {
            return user.getUsername();
        }
    
        @Override
        public boolean isAccountNonExpired() {
            return true;
        }
    
        @Override
        public boolean isAccountNonLocked() {
            return true;
        }
    
        @Override
        public boolean isCredentialsNonExpired() {
            return true;
        }
    
        @Override
        public boolean isEnabled() {
            return true;
        }
    }
    
posted @   易文杰  阅读(188)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示