JWT和Security 登录权限判断和token访问和让token失效

简介: JWT和Security 登录权限判断和token访问和让token失效
Spring Security

Spring Security是一个功能强大且高度可定制的身份验证和访问控制框架。它是用于保护基于Spring的应用程序的实际标准。 Spring Security是一个框架,致力于为Java应用程序提供身份验证和授权。与所有Spring项目一样,Spring Security的真正强大之处在于可以轻松扩展以满足自定义要求

JWT无状态的单点登录

jwt生成的token有有效期,如果没有到有效期怎么让token失效?

用户每次登录都把token对应保存到redis(username,token)

每次判断token是否有效,有效再次判断token是否和redis是否一致,不一致则token失效


流程
  1. 注册 username,password(加密后的)存到数据库
  2. 登录 通过username 和 password 比较判断是否正确,正确返回用户信息 token(jwt生成)
  3. 访问(需要传入token)通过token生成对应的UsernamePasswordAuthenticationToken并设置到SecurityContext

中去

  1. 设置某一个方法的访问权限

@PreAuthorize(“hasAuthority(‘ROLE_XYZ’)”)与@PreAuthorize(“hasRole(‘XYZ’)”)相同

用到的方法
configure(HttpSecurity http)
  http.cors()
                .and()
                // 关闭csrf
                .csrf().disable()
                // 关闭session
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .exceptionHandling()
                .accessDeniedHandler(deniedHandler)
                .authenticationEntryPoint(entryPoint)
                .and()
                .authorizeRequests()
                // 需要角色为ADMIN才能删除该资源
                .antMatchers(HttpMethod.DELETE, "/tasks/**").hasAnyRole(ROLE_ADMIN)
                // 测试用资源,需要验证了的用户才能访问
                .antMatchers("/tasks/**").authenticated()
                // 其他都放行了
                .anyRequest().permitAll()
                .and()
                .formLogin()
                .loginPage("/api/user/login")
                .usernameParameter("user")
                .passwordParameter("pwd")
                .successHandler(successHandler)
                .failureHandler(failureHandler)
                .permitAll()
                .and()
                .logout()//默认注销行为为logout
                .logoutUrl("/api/user/logout")
                .logoutSuccessHandler(logoutSuccessHandler)
                .and()
                // 添加到过滤链中
                // 先是UsernamePasswordAuthenticationFilter用于login校验
                // 再通过OncePerRequestFilter,对其他请求过滤
                .addFilter(new JwtPreAuthFilter(authenticationManager(), getJwtTokenUtil()));

登录 authenticationSuccessHandler loadUserByUsername

http://127.0.0.1:8087/api/user/login

//匹配
   List<SimpleGrantedAuthority> simpleGrantedAuthorities = new ArrayList<>();
  simpleGrantedAuthorities.add(new SimpleGrantedAuthority(ROLE_ADMIN));
    AdminUser user = AdminUser.getInstance();
    return new User(user.getUsername(), user.getPassword(), simpleGrantedAuthorities);
    
//返回数据
 response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
  String s = JwtTokenUtil.TOKEN_PREFIX+jwtTokenUtil.generateToken(SecurityUtils.getCurrentUser());
   response.getWriter().write(JSONObject.toJSONString(RUtil.success(AdminUser.getInstance().setToken(s))));
通过token访问 doFilterInternal
@Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain chain) throws IOException, ServletException {
        String tokenHeader = request.getHeader(JwtTokenUtil.TOKEN_HEADER);
        System.out.println("tokenHeader:" + tokenHeader);
        // 如果请求头中没有Authorization信息则直接放行了
        if (tokenHeader == null || !tokenHeader.startsWith(JwtTokenUtil.TOKEN_PREFIX)) {
            chain.doFilter(request, response);
            return;
        }
        // 如果请求头中有token,则进行解析,并且设置认证信息
        SecurityContextHolder.getContext().setAuthentication(getAuthentication(tokenHeader));
        super.doFilterInternal(request, response, chain);
    }

    /**
     * description: 读取Token信息,创建UsernamePasswordAuthenticationToken对象
     *
     * @param tokenHeader
     * @return org.springframework.security.authentication.UsernamePasswordAuthenticationToken
     */
    private UsernamePasswordAuthenticationToken getAuthentication(String tokenHeader) {
        //解析Token时将“Bearer ”前缀去掉
        String token = tokenHeader.replace(JwtTokenUtil.TOKEN_PREFIX, "");
        String username = jwtTokenUtil.getUserNameFromToken(token);
        List<String> roles = jwtTokenUtil.getUserRole(token);
        Collection<GrantedAuthority> authorities = new HashSet<>();
        if (roles != null) {
            for (String role : roles) {
                // authorities.add(new SimpleGrantedAuthority(ROLE_PRE+role));
                //hasRole('admin')

                //hasAuthority('admin')
                //@PreAuthorize("hasAuthority('ROLE_XYZ')")与@PreAuthorize("hasRole('XYZ')")相同
                authorities.add(new SimpleGrantedAuthority(role));
            }
        }
        if (username != null) {
            return new UsernamePasswordAuthenticationToken(username, null, authorities);
        }
        return null;
    }
方法设置权限
  @ApiOperation(value = "测试权限接口hasAuthority")
    @PostMapping(value = "/testPreAuthorize")
    // @PreAuthorize("hasAuthority('administrator') or hasAuthority('admin')")
    // @PreAuthorize("hasAuthority('administrator') and hasAuthority('admin')")
    //@PreAuthorize("hasAnyRole('administrator','admin')")
    //@PreAuthorize("hasAnyAuthority('administrator','admin')")
    @PreAuthorize("hasAuthority('admin')")
    public R<String> testPreAuthorize() {
        return RUtil.success("admin" + SecurityContextHolder.getContext().getAuthentication().getPrincipal());
    }


    @ApiOperation(value = "测试权限接口")
    @PostMapping(value = "/testPreAuthorizeRole")
    @PreAuthorize("hasRole('admin')")
    public R<String> testPreAuthorizeRole() {
        return RUtil.success("admin" + SecurityContextHolder.getContext().getAuthentication().getPrincipal());
    }
验证密码流程

判断用户输入的密码是否正确 原密码和加密后的密码匹配

  1. UserDetails userDetails(数据库存储的密码-加密后的密码) 通过 (UserDetailsService-loadUserByUsername)赋值
  2. UsernamePasswordAuthenticationToken authentication(前端获取到的密码) 通过过滤器(UsernamePasswordAuthenticationToken->attemptAuthentication)赋值
  3. 对比密码 DaoAuthenticationProvider->additionalAuthenticationChecks->

@Sup

@SuppressWarnings("deprecation")
  protected void additionalAuthenticationChecks(UserDetails userDetails,
      UsernamePasswordAuthenticationToken authentication)
      throws AuthenticationException {
    if (authentication.getCredentials() == null) {
      logger.debug("Authentication failed: no credentials provided");

      throw new BadCredentialsException(messages.getMessage(
          "AbstractUserDetailsAuthenticationProvider.badCredentials",
          "Bad credentials"));
    }

    String presentedPassword = authentication.getCredentials().toString();

    if (!passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
      logger.debug("Authentication failed: password does not match stored value");

      throw new BadCredentialsException(messages.getMessage(
          "AbstractUserDetailsAuthenticationProvider.badCredentials",
          "Bad credentials"));
    }
  }
动态权限管理
1. DynamicSecurityService

获取所有路径对应的资源并存放到map中

修改或者删除后台资源以后需要清空动态资源重新获取

2. OncePerRequestFilter->doFilterInternal

JWT登录授权过滤器

通过UserDetailsService获取UserDetails并设置setAuthentication(UsernamePasswordAuthenticationToken)

FilterChain.doFilter

ExceptionTranslationFilter->handleSpringSecurityException-handleAuthenticationException&handleAccessDeniedException


3. AbstractSecurityInterceptor&FIlter->beforeInvocation,doFilter

doFilter中调用beforeInvocation

beforeInvocation调用getAttributes,attemptAuthorization

attemptAuthorization 调用decide

4. AuthenticationEntryPoint->commence
5. AccessDeniedHandler->handle
6. FilterInvocationSecurityMetadataSource->getAttributes

如果配置的资源map为空需要重新获取

获取访问该路径所需资源

7. AccessDecisionManager->decide

将访问所需资源和用户拥有资源进行比对

通过1获取访问所需的资源


web.ignoring()和permitAll() 区别

web.ignoring(),一般配置静态资源路径 配置以后忽略所有WebSecurity相关的过滤器

permitAll() 放行 不去做验证是否可以访问(UsernamePasswordAuthenticationToken),但还是会经过过滤器

相关文章
|
7月前
|
JSON 安全 Java
什么是JWT?如何使用Spring Boot Security实现它?
什么是JWT?如何使用Spring Boot Security实现它?
1301 5
|
11月前
|
SQL Java 测试技术
在Spring boot中 使用JWT和过滤器实现登录认证
在Spring boot中 使用JWT和过滤器实现登录认证
547 0
|
6月前
|
XML JavaScript Java
SpringBoot集成Shiro权限+Jwt认证
本文主要描述如何快速基于SpringBoot 2.5.X版本集成Shiro+JWT框架,让大家快速实现无状态登陆和接口权限认证主体框架,具体业务细节未实现,大家按照实际项目补充。
372 11
|
8月前
|
JSON 安全 Go
Go语言中使用JWT鉴权、Token刷新完整示例,拿去直接用!
本文介绍了如何在 Go 语言中使用 Gin 框架实现 JWT 用户认证和安全保护。JWT(JSON Web Token)是一种轻量、高效的认证与授权解决方案,特别适合微服务架构。文章详细讲解了 JWT 的基本概念、结构以及如何在 Gin 中生成、解析和刷新 JWT。通过示例代码,展示了如何在实际项目中应用 JWT,确保用户身份验证和数据安全。完整代码可在 GitHub 仓库中查看。
1292 1
|
10月前
|
存储 中间件 API
ThinkPHP 集成 jwt 技术 token 验证
本文介绍了在ThinkPHP框架中集成JWT技术进行token验证的流程,包括安装JWT扩展、创建Token服务类、编写中间件进行Token校验、配置路由中间件以及测试Token验证的步骤和代码示例。
ThinkPHP 集成 jwt 技术 token 验证
|
10月前
|
JSON 安全 数据安全/隐私保护
从0到1搭建权限管理系统系列三 .net8 JWT创建Token并使用
【9月更文挑战第22天】在.NET 8中,从零开始搭建权限管理系统并使用JWT(JSON Web Tokens)创建Token是关键步骤。JWT是一种开放标准(RFC 7519),用于安全传输信息,由头部、载荷和签名三部分组成。首先需安装`Microsoft.AspNetCore.Authentication.JwtBearer`包,并在`Program.cs`中配置JWT服务。接着,创建一个静态方法`GenerateToken`生成包含用户名和角色的Token。最后,在控制器中使用`[Authorize]`属性验证和解析Token,从而实现身份验证和授权功能。
840 4
|
9月前
|
JavaScript
Node.js单点登录SSO详解:Session、JWT、CORS让登录更简单(二)
Node.js单点登录SSO详解:Session、JWT、CORS让登录更简单(一)
253 0
|
9月前
|
存储 JSON JavaScript
Node.js单点登录SSO详解:Session、JWT、CORS让登录更简单(一)
Node.js单点登录SSO详解:Session、JWT、CORS让登录更简单(一)
302 0
|
11月前
【Azure APIM】在APIM中实现JWT验证不通过时跳转到Azure登录页面
【Azure APIM】在APIM中实现JWT验证不通过时跳转到Azure登录页面
|
11月前
|
API
【Azure Developer】记录一段验证AAD JWT Token时需要设置代理获取openid-configuration内容
【Azure Developer】记录一段验证AAD JWT Token时需要设置代理获取openid-configuration内容