首页 > 其他分享 >SpringSecurity使用JWT

SpringSecurity使用JWT

时间:2023-06-04 14:22:17浏览次数:42  
标签:String JWT TOKEN SpringSecurity public token 使用 response out

SpringSecurity的UsernamePasswordAuthenticationFilter用于处理认证。要整合JWT,只需在认证成功后生成TOKEN并通过响应头写回客户端。在新增一个过滤器用于校验TOKEN。

新建SpringBoot项目,添加依赖:

   <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>io.jsonwebtoken</groupId>
        <artifactId>jjwt</artifactId>
        <version>0.9.0</version>
    </dependency>

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>

     <dependency>
        <groupId>javax.xml.bind</groupId>
        <artifactId>jaxb-api</artifactId>
        <version>2.3.1</version>
    </dependency>

我使用的JAVA版本是17,版本较高,需要添加jaxb-api依赖。

 

在application.properties配置

#60分钟
token.expire=3600000
token.key=123456HJKsdsf,';dfs

配置TOKEN的有效时间和秘钥。

 

增加TOKEN管理接口:

public interface TokenManager {
    public String createToken(String username);

    public String getUserFromToken(String token);
}

增加实现类:

@Service
@Slf4j
public class JwtTokenManager implements TokenManager {

    @Value("${token.expire}")
    private long tokenExpiration = 3600;
    @Value("${token.key}")
    private String tokenSignKey;

    @Override
    public String createToken(String username) {
        String token = Jwts.builder().setSubject(username)
                .setExpiration(new Date(System.currentTimeMillis() + tokenExpiration))
                .signWith(SignatureAlgorithm.HS512, tokenSignKey).compressWith(CompressionCodecs.GZIP).compact();
        log.info("用户:{}生成token:{}", username, token);
        return token;
    }


    @Override
    public String getUserFromToken(String token) {
        String user = Jwts.parser().setSigningKey(tokenSignKey).parseClaimsJws(token).getBody().getSubject();
        log.info("从token:{}解析的用户名:{}", token, user);
        return user;
    }
}

 

增加常量:

public  final class GlobalConstant {

    private GlobalConstant() {

    }
    public final static String TOKEN_NAME = "MY_TOKEN";

}

保存TOKEN的名字。

 

配置SpringSecurity:

@Configuration
public class MySecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private TokenManager tokenManager;

    @Bean
    public PasswordEncoder passwordEncoder() {
        return NoOpPasswordEncoder.getInstance();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("admin")
                .password("123")
                .roles("USER")
                .and()
                .withUser("user")
                .password("123")
                .roles("TEMP");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest()
                .authenticated()
                .and()
                .formLogin()
                .loginProcessingUrl("/login")
                .successHandler((request,response,authentication) -> {
                    UserDetails  userDetails = (UserDetails) authentication.getPrincipal();
                    String token = tokenManager.createToken(userDetails.getUsername());
                    response.setHeader(TOKEN_NAME, token);

                    response.setContentType("application/json;charset=utf-8");
                    PrintWriter out = response.getWriter();
                    out.write("登录成功");
                    out.flush();
                    out.close();
                })
                .failureHandler(((request, response, exception) -> {
                    response.setContentType("application/json;charset=utf-8");
                    PrintWriter out = response.getWriter();
                    out.write(exception.getMessage());
                    out.flush();
                    out.close();
                }))
                .permitAll()
                .and()
                .logout()
                .logoutSuccessHandler(((request, response, authentication) -> {
                    response.setContentType("application/json;charset=utf-8");
                    PrintWriter out = response.getWriter();
                    out.write("注销成功");
                    out.flush();
                    out.close();
                }))
                .permitAll()
                .and()
                .exceptionHandling()
                .authenticationEntryPoint(((request, response, authException) -> {
                    response.setContentType("application/json;charset=utf-8");
                    PrintWriter out = response.getWriter();
                    out.write("尚未登录,请先登录");
                    out.flush();
                    out.close();
                }))
                .and()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .csrf()
                .disable()
                ;


        JwtAuthencationFilter jwtAuthencationFilter = new JwtAuthencationFilter(tokenManager, http.getSharedObject(UserDetailsService.class));
        http.addFilterBefore(jwtAuthencationFilter, UsernamePasswordAuthenticationFilter.class);
    }
}

为了简单,将用户信息保存到内存中。successHandler用于处理认证成功后的操作,这里生成TOKEN并写到客户端。failureHandler用于处理认证失败的操作。logoutSuccessHandler用于处理注销成功后的操作。authenticationEntryPoint用于处理未登录的操作。JwtAuthencationFilter用于校验TOKEN。JwtAuthencationFilter加到UsernamePasswordAuthenticationFilter的前面。还配置了session的创建策略为SessionCreationPolicy.STATELESS,即不创建Session。

 

public class JwtAuthencationFilter extends OncePerRequestFilter {

    private TokenManager tokenManager;

    private UserDetailsService userDetailsService;

    public JwtAuthencationFilter(TokenManager tokenManager, UserDetailsService userDetailsService) {
        this.tokenManager = tokenManager;
        this.userDetailsService = userDetailsService;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String token = request.getHeader(TOKEN_NAME);

        if (token != null) {
            String username = tokenManager.getUserFromToken(token);

            if (username != null) {
                UserDetails userDetails = userDetailsService.loadUserByUsername(username);
                if (userDetails == null) {
                    throw new RuntimeException("非法TOKEN");
                }

                SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(username, token, new ArrayList<>()));
            } else {
                throw new RuntimeException("非法TOKEN");
            }
        }

        filterChain.doFilter(request, response);
    }
}

JwtAuthencationFilter从TOKEN中取出用户名,校验用户是否有效。这里还可以将用户保存到redis,避免每次都要查询数据库。还可以校验TOKEN的有效性,以及TOKEN的续期。如果访问不带TOKEN,则由配置的authenticationEntryPoint处理未登录情形。

标签:String,JWT,TOKEN,SpringSecurity,public,token,使用,response,out
From: https://www.cnblogs.com/shigongp/p/17455602.html

相关文章

  • 使用vue出现Uncaught TypeError: Vue is not a constructor错误
    原因是vue2和vue3写法不对正确是<!DOCTYPEhtml><html>   <head>      <metacharset="utf-8">      <title></title>      <scripttype="text/javascript"src="https://unpkg.com/vue@next"></s......
  • 极客时间--golang并发实战课--Mutex的常见使用错误场景
    1.Lock/Unlock没有成对出现,就意味着会出现死锁的情况,或者是因为Unlock一个未加锁的Mutex而导致panic。2.第二种误用是Copy已使用的Mutex。Packagesync的同步原语在使用后是不能复制的。原因在于,Mutex是一个有状态的对象,它的state字段记录这个锁的状态。如果你要复......
  • socket.io-client+Vue3使用
    客户端代码服务端代码......
  • 带你入逆向坑,怎样在win10上安装并使用Z3库
    点点关注,本萝莉就亲亲你上回说到三道不同平台的Reverse题目带你入逆向坑本文主要是写一下自己在使用Z3约束器来解方程时遇到的坑失败了好多次 才流泪写下这篇教程(好几次都想放弃)  避免大家和我一样浪费时间百度谷歌了好久也没找到方法话说大佬们是不是有什么技巧ps:大家给......
  • duboo注解使用详解
    一、背景随着互联网的发展,网站应用的规模不断扩大,常规的垂直应用架构已无法应对,分布式服务架构以及流动计算架构势在必行。当越来越的的接口与实现类的增加后,duboo的xml配置会越来越多,为了防止几百几千行的代码,减少开发人员配置xml的工作量,使用duboo的注解模式,减少配置多出问题多的......
  • mysql-5.6.13在windows平台下的安装、使用(图解)
    一、首先电脑要具备.NetFramework4以上环境二、MySQL下载、安装、执行1.下载http://cdn.mysql.com/Downloads/MySQL-5.6/mysql-5.6.13-win32.zip2.安装点击“installMySQLproducts”选中“Iacceptthelicenceterms”,点击next选中“Skipthecheckforupdate......
  • 云原生之使用Docker部署TeaKKi知识文档管理工具
    (云原生之使用Docker部署TeaKKi知识文档管理工具)一、TeaKKi介绍Teakki是一款知识文档管理工具,当前支持企业本地私有化部署,免费试用有效期30天。适用于团队,企业的知识协作和管理.,为你构建团队的知识库!TeaKKi专注知识知识协作,让团队知识协作变得简单高效。二、检查docker......
  • Java 容器详解:使用与案例
    Java容器是一套工具,用于存储数据和对象。可以与C++的STL类比。Java容器也称为JavaCollectionFramework(JCF)。除了存储对象的容器之外,还提供了一套工具类,用于处理和操作容器中的对象。总体来说,这是一个框架,它包含了Java对象容器和工具类。一、概览容器主要包括Collection......
  • 安全测试--fiddler工具学习和使用
    平常功能测试或者在进行安全相关测试时,会抓一些接口数据,来进行辅助测试。使用较多的抓包工具是fiddle,所以这里介绍了一下fiddler原理,并介绍一些工作上经常使用的fiddler功能(工具栏、抓https的设置、过滤器、重放、段点、会话保存等功能,手机抓包的功能在另外一篇随笔里)。......
  • 使用 HTTP Boot,可以批量部署操作系统和软件,提高管理效率和降低成本。下面是常用的几种
    使用HTTPBoot,可以批量部署操作系统和软件,提高管理效率和降低成本。下面是常用的几种批量部署方案:使用WindowsDeploymentServices(WDS):WDS是一种基于微软技术的网络引导服务,可以快速部署Windows操作系统和应用程序。通过配置WDS服务器和映像文件,可以实现从远程服务器引......