世界球精选!Spring Boot的安全配置(三)

JWT

JWT(JSON Web Token)是一种用于在网络中传输安全信息的开放标准(RFC 7519)。它可以在各个服务之间安全地传递用户认证信息,因为它使用数字签名来验证信息的真实性和完整性。

JWT有三个部分,每个部分用点(.)分隔:

Header:通常包含JWT使用的签名算法和令牌类型。Payload:包含有关用户或其他主题的声明信息。声明是有关实体(通常是用户)和其他数据的JSON对象。声明被编码为JSON,然后使用Base64 URL编码。Signature:用于验证消息是否未被篡改并且来自预期的发送者。签名由使用Header中指定的算法和秘钥对Header和Payload进行加密产生。

在Spring Boot中,您可以使用Spring Security和jjwt库来实现JWT的认证和授权。下面是一个使用JWT的示例:


(资料图片)

@Configuration@EnableWebSecuritypublic class SecurityConfig extends WebSecurityConfigurerAdapter {    @Value("${jwt.secret}")    private String jwtSecret;    @Override    protected void configure(HttpSecurity http) throws Exception {        http.csrf().disable()            .authorizeRequests()            .antMatchers(HttpMethod.POST, "/api/authenticate").permitAll()            .anyRequest().authenticated()            .and()            .addFilter(new JwtAuthenticationFilter(authenticationManager(), jwtSecret))            .addFilter(new JwtAuthorizationFilter(authenticationManager(), jwtSecret))            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);    }    @Override    public void configure(AuthenticationManagerBuilder auth) throws Exception {        auth.authenticationProvider(new JwtAuthenticationProvider(jwtSecret));    }}

在上面的示例中,SecurityConfig类继承了WebSecurityConfigurerAdapter并使用了@EnableWebSecurity注解启用Spring Security。configure()方法使用HttpSecurity对象来配置HTTP请求的安全性。.csrf().disable()禁用了CSRF保护。.authorizeRequests()表示进行授权请求。.antMatchers(HttpMethod.POST, "/api/authenticate").permitAll()表示允许POST请求到/api/authenticate路径。.anyRequest().authenticated()表示要求所有其他请求都需要身份验证。.addFilter(new JwtAuthenticationFilter(authenticationManager(), jwtSecret)).addFilter(new JwtAuthorizationFilter(authenticationManager(), jwtSecret))分别添加JWT认证和授权过滤器。.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)指定了会话管理策略。

configure()方法中还有一个configure(AuthenticationManagerBuilder auth)方法,它使用JwtAuthenticationProvider类配置身份验证。在这里,jwtSecret被注入到JwtAuthenticationProvider构造函数中,以便在认证过程中使用。

下面是JwtAuthenticationFilterJwtAuthorizationFilter的实现:

public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter {    private final AuthenticationManager authenticationManager;    private final String jwtSecret;    public JwtAuthenticationFilter(AuthenticationManager authenticationManager, String jwtSecret) {        this.authenticationManager = authenticationManager;        this.jwtSecret = jwtSecret;        setFilterProcessesUrl("/api/authenticate");    }    @Override    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) {        try {            LoginRequest loginRequest = new ObjectMapper().readValue(request.getInputStream(), LoginRequest.class);            Authentication authentication = new UsernamePasswordAuthenticationToken(                    loginRequest.getUsername(),                    loginRequest.getPassword()            );            return authenticationManager.authenticate(authentication);        } catch (IOException e) {            throw new RuntimeException(e);        }    }    @Override    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) {        UserPrincipal userPrincipal = (UserPrincipal) authResult.getPrincipal();        String token = Jwts.builder()                .setSubject(userPrincipal.getUsername())                .setIssuedAt(new Date())                .setExpiration(new Date(System.currentTimeMillis() + 864000000))                .signWith(SignatureAlgorithm.HS512, jwtSecret)                .compact();        response.addHeader("Authorization", "Bearer " + token);    }}

JwtAuthenticationFilter类继承了UsernamePasswordAuthenticationFilter类,它用于处理基于用户名和密码的身份验证。它还使用AuthenticationManager来验证用户名和密码是否正确。jwtSecret在构造函数中被注入,用于生成JWT令牌。

attemptAuthentication()方法中,LoginRequest对象被反序列化为从请求中获取的用户名和密码。这些值被封装到UsernamePasswordAuthenticationToken中,并传递给AuthenticationManager以验证用户身份。

在身份验证成功后,successfulAuthentication()方法被调用。在这里,UserPrincipal对象被从Authentication对象中获取,然后使用Jwts类生成JWT令牌。setSubject()方法将用户名设置为JWT主题。setIssuedAt()方法设置JWT令牌的发行时间。setExpiration()方法设置JWT令牌的到期时间。signWith()方法使用HS512算法和jwtSecret密钥对JWT令牌进行签名。最后,JWT令牌被添加到响应标头中。

下面是JwtAuthorizationFilter的实现:

public class JwtAuthorizationFilter extends BasicAuthenticationFilter {    private final String jwtSecret;    public JwtAuthorizationFilter(AuthenticationManager authenticationManager, String jwtSecret) {        super(authenticationManager);        this.jwtSecret = jwtSecret;    }    @Override    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {        String authorizationHeader = request.getHeader("Authorization");        if (authorizationHeader == null || !authorizationHeader.startsWith("Bearer ")) {            chain.doFilter(request, response);            return;        }        String token = authorizationHeader.replace("Bearer ", "");        try {            Jws claimsJws = Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(token);            String username = claimsJws.getBody().getSubject();            List authorities = (List) claimsJws.getBody().get("authorities");            List grantedAuthorities = authorities.stream()                    .map(SimpleGrantedAuthority::new)                    .collect(Collectors.toList());            Authentication authentication = new UsernamePasswordAuthenticationToken(username, null, grantedAuthorities);            SecurityContextHolder.getContext().setAuthentication(authentication);            chain.doFilter(request, response);        } catch (JwtException e) {            response.setStatus(HttpStatus.UNAUTHORIZED.value());        }    }}

JwtAuthorizationFilter类继承了BasicAuthenticationFilter类,并覆盖了doFilterInternal()方法。在这个方法中,请求头中的Authorization标头被解析,如果它不是以Bearer开头,则直接传递给过滤器链。否则,从令牌中解析出主题(用户名)和授权信息,然后创建一个包含用户身份验证和授权信息的Authentication对象,并将其设置到SecurityContextHolder中。

如果JWT令牌无效,JwtException将被抛出,并返回HTTP 401未经授权的错误。

标签:

最近更新

世界球精选!Spring Boot的安全配置(三)
2023-04-07 04:24:14
环球聚焦:樱桃科学与生产
2023-04-06 23:18:26
姆巴佩抗议巴黎:我的采访视频,未被告知用途 环球关注
2023-04-06 21:33:34
天天热资讯!明天4月7号,雨雪范围已经确认,清明节后还会冷吗?农谚咋说的?
2023-04-06 20:06:50
新产业: 关于回购注销部分限制性股票减少注册资本暨通知债权人的公告|环球热闻
2023-04-06 19:12:04
香港印花税退税机制今刊宪 外来人才买房转永居可退税
2023-04-06 17:53:14
2023年新疆注会考试报名入口
2023-04-06 16:58:31
天天热头条丨巴西圣保罗瓜鲁雅一银行被武装犯罪分子炸毁
2023-04-06 16:17:20
天津津南区公租房2023第二季度摇号时间
2023-04-06 14:28:55
乡间笛鸣公交来 全省6000多条运营线路串村连镇,让农民“出家门、上车门、进城门”
2023-04-06 13:20:15
30天后再探湖北4S店:坐高铁买车消失,逼出随缘式卖车 | 车市远光灯
2023-04-06 12:17:06
全球即时看!谭爱玲:为妹妹家撑起一片天,用志愿服务去“感恩”
2023-04-06 11:16:57
2023天津野生朋友派对活动攻略
2023-04-06 10:46:54
大瓜!爆料谭松韵已婚,老公是万达董事华汉,男方叫万达二儿子 天天热门
2023-04-06 10:01:46
瑞萨电子率先在环境传感器中支持公共建筑空气质量标准-环球头条
2023-04-06 09:11:42
环球微头条丨低价充值套路多 高额优惠暗藏消费陷阱
2023-04-06 08:09:39
内外大升级 新款北京X7将于4月16日上市 天天新视野
2023-04-06 06:19:05
炖肉时产生的浮沫,是精华还是脏东西?要不要撇去? 全球看点
2023-04-06 03:03:00
林黛玉经典语录怼人_林黛玉怼人经典语录
2023-04-05 22:21:49
观速讯丨费舍尔:能看出疲惫对湖人的影响,一度只有勒布朗持球
2023-04-05 20:47:36
世界看点:榜样7人物事迹介绍_紫乃宫天音的介绍
2023-04-05 18:50:59
壑怎么读_骰怎么读
2023-04-05 17:07:02
岚图3月交付量达到3027辆 同比上涨116%|环球即时
2023-04-05 15:45:05
国家发改委:内部没有“全国统一大市场规划司”相关机构 全球今日报
2023-04-05 14:08:50
【网络中国节·清明】共同参与!2023年贵阳贵安“网上祭英烈”活动_快播报
2023-04-05 12:24:00
明道为陈乔恩庆44岁生日 梦回王子变青蛙|天天百事通
2023-04-05 10:58:20
当前报道:苏门家风 浸润人心
2023-04-05 09:29:45
环球视点!我国国际客运航班通航国家和地区恢复至疫情前约八成
2023-04-05 08:11:35
世界微资讯!为什么空调遥控器调不上制热?
2023-04-05 06:17:20
血压高的人,少碰2物,多吃3菜,血压平稳,头不晕
2023-04-05 03:47:27