• 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;
          }
      }
      
  • 相关阅读:
    C# 斐波拉契数列
    Visual Studio [即时窗口] & [命令窗口] (Immediate Window & Command Window) 转
    在.NET平台下 有哪些数据持久层框架 (转)
    WebPart 控件之间通讯 笔记
    WebPart的数据库连接问题 转
    C 语言函数要先声明后定义
    C#单例模式的三种写法(转)
    WCF 绑定wshttpbinding
    关于C#中派生类调用基类构造函数的理解 base使用
    ThinkPHP3.* 模型操作相关函数
  • 原文地址:https://www.cnblogs.com/ywjcqq/p/16107309.html
Copyright © 2020-2023  润新知