【SpringSecurity】十一、SpringSecurity集成JWT实现token的方法与校验

这篇具有很好参考价值的文章主要介绍了【SpringSecurity】十一、SpringSecurity集成JWT实现token的方法与校验。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1、依赖与配置

添加JWT的maven依赖:

<!-- 添加jwt的依赖 -->
<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>3.11.0</version>
</dependency>

application.yaml中配置密钥的值,方便代码中引用和后续更改:

jwt:
  secretKey: mykey

2、JWT工具类

这里的命名改为JWTService好点,Utils命名似乎偏静态方法一点。

@Component
@Slf4j
public class JwtUtils {
    //算法密钥
    @Value("${jwt.secretKey}")
    private String jwtSecretKey;

    /**
     * 创建jwt
     *
     * @param userInfo 用户信息
     * @param authList 用户权限列表
     * 根据登录用户的数据库信息和权限信息,加上服务端密钥,创建token
     * @return 返回jwt(JSON WEB TOKEN)
     */
    public String createToken(String userInfo, List<String> authList) {
        //创建时间
        Date currentTime = new Date();
        //过期时间,5分钟后过期
        Date expireTime = new Date(currentTime.getTime() + (1000 * 60 * 5));
        //jwt的header信息
        Map<String, Object> headerClaims = new HashMap<>();
        headerClaims.put("type", "JWT");
        headerClaims.put("alg", "HS256");
        //创建jwt
        return JWT.create()
                .withHeader(headerClaims) // 头部信息
                .withIssuedAt(currentTime) //已注册声明:签发日期,发行日期
                .withExpiresAt(expireTime) //已注册声明 过期时间
                .withIssuer("llg")  //已注册声明,签发人
                .withClaim("userInfo", userInfo) //私有声明,可以自己定义
                .withClaim("authList", authList) //私有声明,可以自定义
                .sign(Algorithm.HMAC256(jwtSecretKey)); // 签名,使用HS256算法签名,并使用密钥
        //HS256是一种对称算法,这意味着只有一个密钥,在双方之间共享。 使用相同的密钥生成签名并对其进行验证。 应特别注意钥匙是否保密。
    }

    /**
     * 验证jwt的签名,简称验签
     * @param token 需要验签的jwt
     * @return 验签结果
     */
    public boolean verifyToken(String token) {
        //获取验签类对象
        JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(jwtSecretKey)).build();
        try {
            //验签,如果不报错,则说明jwt是合法的,而且也没有过期
            DecodedJWT decodedJWT = jwtVerifier.verify(token);
            return true;
        } catch (JWTVerificationException e) {
            //如果报错说明jwt 为非法的,或者已过期(已过期也属于非法的)
            log.error("验签失败:{}", token);
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 从token中获取用户信息
     * 这个userInfo是创建token时我自己塞进去的
     * @param token jwt
     * @return 用户信息
     */
    public String getUserInfo(String token) {
        //创建jwt验签对象
        JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(jwtSecretKey)).build();
        try {
            //验签(主要为了同时的获取解析的结果)
            DecodedJWT decodedJWT = jwtVerifier.verify(token);
            //获取payload中userInfo的值,并返回
            return decodedJWT.getClaim("userInfo").asString();
        } catch (JWTVerificationException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 获取用户权限
     *
     * @param token
     * @return
     */
    public List<String> getUserAuth(String token) {
        //创建jwt验签对象
        JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(jwtSecretKey)).build();
        try {
            //验签(主要为了同时的获取解析的结果)
            DecodedJWT decodedJWT = jwtVerifier.verify(token);
            //获取payload中的自定义数据authList(权限列表),并返回
            return decodedJWT.getClaim("authList").asList(String.class);
        } catch (JWTVerificationException e) {
            e.printStackTrace();
        }
        return null;
    }

}

再贴一下下统一结果类的定义:

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class HttpResult implements Serializable {
    private Integer code; //响应码
    private String msg; //响应消息
    private Object data; //响应对象
}

下面是安全用户类,用于在数据库的用户对象类SysUser和返给框架的官方对象类UserDetails之间做过渡转换。UserDetails <====> SecurityUser <====> SysUser

@Setter
public class SecurityUser implements UserDetails {

    private  final SysUser sysUser;

	private List<SimpleGrantedAuthority> simpleGrantedAuthorities;

    public SecurityUser(SysUser sysUser) {
        this.sysUser=sysUser;
    }
	public SysUser getSysUser() {
    return sysUser;
}


    @Override
	public Collection<? extends GrantedAuthority> getAuthorities() {
    	return simpleGrantedAuthorities;
	}



    @Override
    public String getPassword() {
        String userPassword=this.sysUser.getPassword();
		//注意清除密码
		this.sysUser.setPassword(null);
		return userPassword;

    }

    @Override
    public String getUsername() {
        return sysUser.getUsername();
    }

    @Override
    public boolean isAccountNonExpired() {
        return sysUser.getAccountNoExpired().equals(1);
    }

    @Override
    public boolean isAccountNonLocked() {
        return sysUser.getAccountNoLocked().equals(1);
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return sysUser.getCredentialsNoExpired().equals(1);
    }

    @Override
    public boolean isEnabled() {
        return sysUser.getEnabled().equals(1);
    }
}


3、认证成功处理器

自定义处理器,实现AuthenticationSuccessHandler,当用户登录认证成功后,会执行这个处理器,即认证成功处理器

/**
 * 认证成功处理器,当用户登录成功后,会执行此处理器
 */
@Component
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
    //使用此工具类进行序列化
    @Resource
    private ObjectMapper objectMapper;
    @Resource
    private JwtUtils jwtUtils;

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        //从认证对象中获取认证用户信息
		//查看前面第六章的UserDetailsService接口的loadUserByUsername方法,
		//返回给框架的是一个自定义的SecurityUser对象(Security实现了UserDetails)
        SecurityUser securityUser = (SecurityUser) authentication.getPrincipal();
        //从SecurityUser中拿出和底层MYSQL挂钩的SysUser类信息
        String userInfo=objectMapper.writeValueAsString(securityUser.getSysUser());
        List<SimpleGrantedAuthority> authorities = (List<SimpleGrantedAuthority>) securityUser.getAuthorities();
        //List<SimpleGrantedAuthority>转List<String>
        List<String> authList=new ArrayList<>();
        for (SimpleGrantedAuthority authority : authorities) {
            authList.add(authority.getAuthority());
        }
		//也可使用stream流代替上面的for循环
		List<String> authList = authorities.stream().map(
		        a -> {
		           return a.getAuthority();
		        }
		).collect(Collectors.toList());

		//也可使用stream流+Lambda表达式
		List<String> authList = authorities.stream()
		.map(SimpleGrantedAuthority::getAuthority)
		.collect(Collectors.toList());


        // 调用前面的JWT工具类方法创建jwt
        String token = jwtUtils.createToken(userInfo,authList);

        //返回给前端token@Builder模式创建对象
        HttpResult httpResult = HttpResult.builder().code(200).msg("OK").data(token).build();
        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=utf-8");
        PrintWriter writer = response.getWriter();
        writer.write(objectMapper.writeValueAsString(httpResult));
        writer.flush();
    }
}


4、创建JWT过滤器

定义JWT过滤器,用来检验一个个对接口的请求中token是否合法,注意放行登录接口:

/**
 * 定义一次性请求过滤器
 */
@Component
@Slf4j
public class JwtCheckFilter extends OncePerRequestFilter {
    @Resource
    private ObjectMapper objectMapper;
    @Resource
    private JwtUtils jwtUtils;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        //获取请求uri
        String requestURI = request.getRequestURI();
        // 如果是登录页面,放行
        if (requestURI.equals("/login")) {
            filterChain.doFilter(request, response);
            return;
        }
        //获取请求头中的Authorization,前端一般这么传,key为Authorization
        String authorization = request.getHeader("Authorization");
        //如果Authorization为空,那么不允许用户访问,直接返回
        if (!StringUtils.hasText(authorization)) {
            printFront(response, "没有登录!");
            return;
        }
        //Authorization 去掉头部的Bearer 信息,获取token值
        String jwtToken = authorization.replace("Bearer ", "");
        //验签
        boolean verifyTokenResult = jwtUtils.verifyToken(jwtToken);
        //验签不成功
        if (!verifyTokenResult) {
            printFront(response, "jwtToken 已过期");
            return;
        }
		//到这儿算是验证通过,但还没结束,还要将信息填充后返给SpringSecurity框架
        //从payload中获取userInfo
        String userInfo = jwtUtils.getUserInfo(jwtToken);
        //从payload中获取授权列表
        List<String> userAuth = jwtUtils.getUserAuth(jwtToken);
        //在认证成功处理器中,创建token时,userInfo里放的是SysUser对象的序列化字符串,这里反序列化
        SysUser sysUser = objectMapper.readValue(userInfo, SysUser.class);
        SecurityUser securityUser = new SecurityUser(sysUser);
        //设置权限
        List<SimpleGrantedAuthority> authList = userAuth.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());
        securityUser.setAuthorityList(authList);

		//填充信息
        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToke = new UsernamePasswordAuthenticationToken(securityUser
                , null, authList);
        //通过安全上下文设置认证信息
        SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToke);
        //继续访问相应的rul等
        filterChain.doFilter(request, response);

    }
    
    /**
 	* 定义一个通过response向前端返回数据的方法
 	* 这里不是controller层,不是你直接返回个结果类就行的,注意区别
 	*/
    private void printFront(HttpServletResponse response, String message) throws IOException {
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json;charset=utf-8");
        PrintWriter writer = response.getWriter();
        HttpResult httpResult = new HttpResult();
        httpResult.setCode(401);
        httpResult.setMsg(message);

        writer.print(objectMapper.writeValueAsString(httpResult));
        writer.flush();
    }
}


上面的过滤器中,除了正常的验签,最后的消息填充与保存在安全上下文,就是下图中的第十步:

【SpringSecurity】十一、SpringSecurity集成JWT实现token的方法与校验,SpringSecurity,SpringSecurity,JWT,token,handler,filter

5、安全配置类

修改下安全配置类,把上面的处理器和过滤器加进来。

@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Resource
    private MyAuthenticationSuccessHandler myAuthenticationSuccessHandler;
    @Resource
    private JwtCheckFilter jwtCheckFilter;
    @Override
    protected void configure(HttpSecurity http) throws Exception {
    	//先看token是否合法,再走框架的用户名密码校验过滤器
        http.addFilterBefore(jwtCheckFilter, UsernamePasswordAuthenticationFilter.class);
        //要是有之间的验证码校验,则它应该在token校验之前
        //认证通过后,走认证成功处理器,颁发token
        http.formLogin().successHandler(myAuthenticationSuccessHandler).permitAll();
        //简单按接口加个权限要求
		http.authorizeRequests()
        .mvcMatchers("/student/**")
        .hasAnyAuthority("student:query","student:update")
		.anyRequest()
		.authenticated(); //任何请求均需要认证(登录成功)才能访问
        http.csrf().disable();  //跨域
        //禁用session方式
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }

   
}

效果:
【SpringSecurity】十一、SpringSecurity集成JWT实现token的方法与校验,SpringSecurity,SpringSecurity,JWT,token,handler,filter

登录认证后返给前端token:

【SpringSecurity】十一、SpringSecurity集成JWT实现token的方法与校验,SpringSecurity,SpringSecurity,JWT,token,handler,filter文章来源地址https://www.toymoban.com/news/detail-694712.html

到了这里,关于【SpringSecurity】十一、SpringSecurity集成JWT实现token的方法与校验的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • JWT主动校验Token是否过期

    JSON Web Token(简称 JWT)是目前最流行的跨域认证解决方案 在前后端分离开发时为什么需要用户认证呢?原因是由于HTTP 协定是不储存状态的(stateless),这意味着当我们透过帐号密码验证一个使用者时,当下一个 request 请求时它就把刚刚的资料忘了。于是我们的程序就不知道谁是谁

    2024年02月09日
    浏览(70)
  • 如何使用 NestJS 集成 Passort 和 JWT Token 实现 HTTP 接口的权限管理

    💡 如果你不希望其他人可以随意进出你的房子,那么你需要给你的房子上个锁。 开发一个接口很容易,开发一个具有安全性的接口却不容易。成熟的后端服务项目最注重的一点就是如何保护系统的数据安全,不能让用户无脑的访问操作所有的数据,这是不合理更是极度危险

    2024年01月22日
    浏览(43)
  • 权限管理 springboot集成springSecurity Oauth2 JWT

    目录 一、SpringSeurity的基础操作 1、引入主要依赖 2、加密器 3、实现自定义登录逻辑 4、访问限制 5、自定义异常处理  6、通过注解的方式配置访问控制 二、Auth2认证方案 1、什么是Auth2认证 2、Oauth2最常用的授权模式  3、依赖引入 4、添加配置类 5、测试 6、存在到Redis里,后续

    2023年04月14日
    浏览(44)
  • SpringBoot 集成 SpringSecurity + MySQL + JWT 附源码,废话不多直接盘

    SpringBoot 集成 SpringSecurity + MySQL + JWT 无太多理论,直接盘 一般用于Web管理系统 可以先看 SpringBoot SpringSecurity 基于内存的使用介绍 本文介绍如何整合 SpringSecurity + MySQL + JWT 数据库脚本:https://gitee.com/VipSoft/VipBoot/blob/develop/vipsoft-security/sql/Security.sql 常规权限管理数据结构设计,

    2024年02月02日
    浏览(38)
  • 分布式环境集成JWT(Java Web Token)

    Token的引入:客户端向服务端请求数据时一般都会加入验证信息,比如客户端在请求的信息中携带用户名、密码,服务端会校验用户名和密码是否正确,校验通过响应该客户端请求。但是每次都携带用户名和密码无疑有些繁琐,而且也不安全,在这种背景下,Token便应运而生。

    2024年02月11日
    浏览(48)
  • SpringSecurity实现角色权限控制(SpringBoot+SpringSecurity+JWT)

    通过 springboot整合jwt和security ,以用户名/密码的方式进行认证和授权。认证通过jwt+数据库的,授权这里使用了两种方式,分别是 SpringSecurity自带的hasRole方法+SecurityConfig 和 我们自定义的 permission+@PreAuthorize注解。 SpringSecurity中的几个重要组件: 1.SecurityContextHolder(class) 用来

    2024年02月05日
    浏览(44)
  • SpringSecurity6+JWT实现前后端分离

    本文使用mybatis-plus作为ORM框架,然后使用springsecurity6作为安全框架,采用JWT作为权限确认的方式。 其实我现在就刚入门这个springsecurity6而已,有许多都没有摸清它的使用,写下这篇文章只是想记录一下,下次想用的时候来看看。 上面说了使用JWT。那就得有一个能生成JWT和检

    2024年02月22日
    浏览(40)
  • 3-1. SpringBoot项目集成【用户身份认证】实战 【技术选型篇】基于Session、Token、JWT怎么选?

    通过第二章2-2. SpringBoot API开发详解 --SpringMVC注解+封装结果+支持跨域+打包,我们实现了基于SpringBoot项目的 API接口开发 ,并实现 API结果统一封装、支持跨域请求 等等功能,接下来开始第三章,主要做用户身份认证,主要实现一套 统一鉴权的用户身份认证的机制 。 我已经提

    2024年01月22日
    浏览(55)
  • SpringSecurity实现前后端分离登录token认证详解

    目录 1. SpringSecurity概述 1.1 权限框架 1.1.1 Apache Shiro 1.1.2 SpringSecurity 1.1.3 权限框架的选择 1.2 授权和认证 1.3 SpringSecurity的功能 2.SpringSecurity 实战 2.1 引入SpringSecurity 2.2 认证 2.2.1 登录校验流程  2.2.2 SpringSecurity完整流程  2.2.3 认证流程详解 2.3 思路分析 2.4 代码实战 2.4.1  自定义

    2024年02月08日
    浏览(50)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包