IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> SpringSecurity+JWT使用 -> 正文阅读

[Java知识库]SpringSecurity+JWT使用

maven依赖

      <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-freemarker</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.0</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.26</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.22</version>
        </dependency>
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.18.2</version>
        </dependency>
        <dependency>
            <groupId>joda-time</groupId>
            <artifactId>joda-time</artifactId>
            <version>2.10.1</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
            <version>2.9.0</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.79</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.12.0</version>
        </dependency>
    </dependencies>

用户登录接口

  /**
     * 登录验证
     *
     * @param username 用户名
     * @param password 密码
     * @return 结果
     */
    public LoginUser login(String username, String password) throws JsonProcessingException {
        // 用户验证
        Authentication authentication = null;
        try {
            authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
        } catch (Exception e) {
            e.printStackTrace();
        }

        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        loginUser.setIpaddr("127.0.0.1");
        loginUser.setLoginTime(new Date().getTime());
        loginUser.setExpireTime(new Date().getTime() + JwtTokenUtils.REDIS_TOKEN_EXPIRATION);
        String token = JwtTokenUtils.createToken(loginUser);
        loginUser.setToken(token);
        // 设置缓存的数据
        redisTemplate.opsForValue().set("sys:token:string:" + token, token, JwtTokenUtils.REDIS_TOKEN_EXPIRATION, TimeUnit.MILLISECONDS);
        // 生成token
        return loginUser;
    }

过滤器实现请求拦截


package com.pug.security.jwt;

import com.pug.config.PugRedisCacheTemplate;
import com.pug.pojo.LoginUser;
import com.pug.pojo.SysLoginUser;
import com.pug.service.jwt.IJwtBlackService;
import com.pug.service.jwt.JwtTokenUtils;
import com.pug.service.user.ILoginUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

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.concurrent.TimeUnit;

/**
 * token过滤器 验证token有效性
 *
 * @author ruoyi
 */
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {


    @Autowired
    @Qualifier("jwtBlackStringService")
    private IJwtBlackService jwtBlackService;
    @Autowired
    private ILoginUserService loginUserService;
    @Autowired
    private PugRedisCacheTemplate pugRedisCacheTemplate;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws ServletException, IOException {

        // 1: 判断当前请求头中是否有token
        String token = JwtTokenUtils.getJwtToken(request);
        if (StringUtils.isEmpty(token)) {
            chain.doFilter(request, response);
        }

        // 判断当前token是不是黑名单里
        if (jwtBlackService.isBlackList(token)) {
            throw new RuntimeException("token is expired black list");
        }

        // 2: 校验token 是否过期 isverfiy = false 代表过期
        boolean isverfiy = JwtTokenUtils.isverfiy(token);
        String tokenKey = "sys:token:string:" + token;
        if (!isverfiy) {
            // 3:如果你过期了就从缓存中去获取token  newh.p.s
            token = pugRedisCacheTemplate.getCacheObject(tokenKey);
        }

        // 获取用户信息
        LoginUser loginUser = JwtTokenUtils.getJwtTokenLoginUser(token);
        if (loginUser == null || pugRedisCacheTemplate.getCacheObject(tokenKey) == null) {
            throw new RuntimeException("token is expired login!!!");
        }

        // 这里负责收集用户对应的信息和权限信息,不做校验和认证
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
        // 覆盖信息
        authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
        // 上下文信息同步
        SecurityContextHolder.getContext().setAuthentication(authenticationToken);

        //2.判断是否需要续期 30分钟就续期
        if (pugRedisCacheTemplate.getExpireTime(tokenKey) < 60 * 40) {
            String newToken = JwtTokenUtils.createToken(loginUser);
            pugRedisCacheTemplate.setCacheObject(tokenKey, newToken, JwtTokenUtils.REDIS_TOKEN_EXPIRATION, TimeUnit.MILLISECONDS);
        }

        chain.doFilter(request, response);
    }
}

security添加拦截器

package com.pug.security.config;

import com.pug.security.handler.MyAccessDeniedHandler;
import com.pug.security.jwt.JwtAuthenticationTokenFilter;
import com.pug.service.jwt.JwtTokenUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.security.web.header.Header;
import org.springframework.security.web.header.writers.StaticHeadersWriter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

import javax.annotation.Resource;
import java.util.Arrays;

/**
 * Created by yykk on 17/1/18.
 */


@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SpringSecurityWebConfiguration extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService loginUserDetailService;
    @Autowired
    private MyAccessDeniedHandler myAccessDeniedHandler;
    @Autowired
    private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
    @Autowired
    private CorsFilter corsFilter;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // 使用自实现的loginUserDetailService来加载数据库中的用户和权限信息
        auth.userDetailsService(loginUserDetailService).passwordEncoder(passwordEncoder());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                // CSRF禁用,因为不使用session
                .csrf().disable()
                // 认证失败处理类
                .exceptionHandling().accessDeniedHandler(myAccessDeniedHandler).and()
                // 基于token,所以不需要session
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                // 过滤请求
                .authorizeRequests()
                // 对于登录login 注册register 验证码captchaImage 允许匿名访问
                .antMatchers("/login", "/register", "/captchaImage").permitAll()
                .antMatchers(
                        HttpMethod.GET,
                        "/",
                        "/*.html",
                        "/**/*.html",
                        "/**/*.css",
                        "/**/*.js",
                        "/profile/**"
                ).permitAll()
                .antMatchers("/swagger-ui.html").permitAll()
                .antMatchers("/swagger-resources/**").permitAll()
                .antMatchers("/webjars/**").permitAll()
                .antMatchers("/*/api-docs").permitAll()
                .antMatchers("/druid/**").permitAll()
                // 除上面外的所有请求全部需要鉴权认证
                .anyRequest().authenticated()
                .and()
                .headers().frameOptions().disable();

        // 添加JWT filter
        http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
        // 添加CORS filter
        http.addFilterBefore(corsFilter , JwtAuthenticationTokenFilter.class);
        http.addFilterBefore(corsFilter , LogoutFilter.class);

    }


    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * 解决 无法直接注入 AuthenticationManager
     *
     * @return
     * @throws Exception
     */
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

}

JWT工具类

package com.pug.service.jwt;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.pug.config.FastJsonUtil;
import com.pug.pojo.LoginUser;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Slf4j
public class JwtTokenUtils {
    // 携带token的请求头名字
    public final static String TOKEN_HEADER = "Authorization";
    //token的前缀
    public final static String TOKEN_PREFIX = "Bearer ";
    // 默认密钥
    public final static String DEFAULT_SECRET = "abcdefghijklmnoqprstuvwxyz";
    // 一分钟
    public final static long ONE_MINUTES = 60 * 1000;
    // App
    public final static long TOKEN_EXPIRATION = 30 * ONE_MINUTES;
    // 续期时间 是token是两倍
    public final static long REDIS_TOKEN_EXPIRATION = 2 * TOKEN_EXPIRATION;
    // 具体加密的算法
    private static final Algorithm algorithm = Algorithm.HMAC256(DEFAULT_SECRET);


    /**
     * SysLoginUser 登录用户
     *
     * @return
     */
    public static String createToken(LoginUser sysLoginUser) throws JsonProcessingException {
        // 2:创建token
        Map<String, Object> headerClaims = new HashMap<>();
        headerClaims.put("typ", "ZIP");
        return JWT.create()
                // 放进去的目的,方便你获取用户信息,避免查询,最后是去掉,
                .withClaim("token", FastJsonUtil.toJSON(sysLoginUser))
                // 后续可能会根据id去查询数据,实时的获取用户信息
                .withHeader(headerClaims)
                // 放一份在主题里
                .withSubject(sysLoginUser.getId() + "")
                // 签发token作者
                .withIssuer(sysLoginUser.getUsername())
                //设置签发时间:iat
                .withIssuedAt(generateCurrentDate())
                //设置过期时间:exp,必须要大于签发时间
                .withExpiresAt(generateExpirationDate())
                //签名信息,采用secret作为私钥
                .sign(algorithm);
    }


    /**
     * 检验token是否正确
     *
     * @param token
     * @return
     */
    public static String parseToken(String token) {
        String userId;
        try {
            userId = getJWTFromToken(token).getSubject();
        } catch (Exception e) {
            userId = null;
        }
        return userId;

    }


    /**
     * 验证是否token过期
     *
     * @param token
     * @return
     */
    public static boolean isverfiy(String token) {
        try {
            JWTVerifier verifier = JWT.require(algorithm).build();
            // 如果token失效了,verify就会出现异常
            verifier.verify(token);
            return true;
        } catch (Exception ex) {
            return false;
        }
    }


    /**
     * @param token
     * @return
     */
    public static DecodedJWT getJWTFromToken(String token) {
        DecodedJWT jwt;
        try {
            JWTVerifier verifier = JWT.require(algorithm).build();
            jwt = verifier.verify(token);
            return jwt;
        } catch (Exception ex) {
            jwt = null;
            log.info("token is expired....");
        }
        return jwt;
    }


    /**
     * 从请求头中获取token
     *
     * @param request
     * @return
     */
    public static String getJwtToken(HttpServletRequest request) {
        // 如果cookie中没有,就去header里面获取
        String header = request.getHeader(TOKEN_HEADER);
        if (header == null || !header.startsWith(TOKEN_PREFIX)) {
            log.info("请求头不含JWT token, 调用下个过滤器");
            return null;
        }

        //去掉token prefix
        String token = header.split(" ")[1].trim();
        return token;
    }


    /**
     * 从请求头中获取token
     *
     * @param token
     * @return
     */
    public static LoginUser getJwtTokenLoginUser(String token) {
        try {
            DecodedJWT jwtFromToken = JwtTokenUtils.getJWTFromToken(token);
            if (jwtFromToken == null) return null;
            Claim claim = jwtFromToken.getClaim("token");
            String asString = claim.asString();
            return FastJsonUtil.toBean(asString, LoginUser.class);
        } catch (Exception ex) {
            return null;
        }
    }


    /**
     * 当前时间
     *
     * @return
     */
    private static Date generateCurrentDate() {
        return new Date();
    }

    /**
     * 过期时间
     *
     * @return
     */
    private static Date generateExpirationDate() {
        return new Date(DateTime.now().getMillis() + TOKEN_EXPIRATION);
    }
}

SpringSecurity授权流程

在这里插入图片描述

security过滤器链

在这里插入图片描述

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-03-21 20:35:37  更:2022-03-21 20:36:22 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 9:29:14-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码