SpringSecurity认证授权完整流程

From: https://www.cnblogs.com/wawaFish666/p/18159570

SpringSecurity认证流程:loadUserByUsername()方法内部实现。

实现步骤:   

  1. 构建一个自定义的service接口,实现SpringSecurity的UserDetailService接口。
  2. 建一个service实现类,实现此loadUserByUsername方法。
  3. 调用登录的login接口,会经过authenticationManager.authenticate(authenticationToken)方法。此方法会调用loadUserByUsername方法。

 

  4.方法内部做用户信息的查询,判断用户名和密码是否正确,这是第一道认证。

 

  5.如果没有查到信息就抛出异常。

  6.如果查到信息了再接着查用户的权限信息,返回权限信息到loginUser实体。

  7.此实体实现了SpringSecurity自带的userDetail接口。实现了getAuthorities方法。

  .8每次查询权限都会调用此方法。

  9.查询到的权限,会被返回到login接口。进行后续操作。

  10.如果认证通过,通过身份信息中的userid生产一个jwt。

  11.把完整的用户信息作为value,token作为key存入redis。

 

  12.后续访问其他接口直接从redis中获取权限。

 

 

如何结合jwt登陆过滤器实现认证:

  1. 定义一个jwt登陆认证器,并交给spring容器管理。
  2. 在SpringSecurity配置中加入过滤器的配置。
  3. 请求会先经过此过滤器,然后会先获取token。
  4. 如果token为空,则放行,调用login接口进行认证的流程。
  5. 如果token不为空则判断是否合法。
  6. 如果token合法就会进行解析,从redis中取到用户信息。再封装进authenticationToken中,后续可以通过securityContext拿到认证信息。再放行。

 

SpringSecurity配置类如何定义?

  1. 密码要进行Bcrypt加密,所以要定义一个Bcrypt的bean。
  2. 认证是在SpringSecurity自带的authenticationManager中执行loadUserByUsername()方法实现的,所以要在此注入authenticationManager这个bean。
  3. 对于登录的login接口应实现匿名放行,其他接口要拦截,需要认证。
  4. 后续的鉴权是通过拿security中的authentication去跟接口上方的注解内标明的权限范围去做对比。

  5. 所以要开启权限注解
  6.  

 

SpringSecurity的原理其实就是一个过滤器链,内部包含了提供各种功能的过滤器。这里我们可以看看入门案例中的过滤器

 

UsernamePasswordAuthenticationFilter:负责处理我们在登陆页面填写了用户名密码后的登陆请求。入门案例的认证工作主要有它负责。

ExceptionTranslationFilter处理过滤器链中抛出的任何AccessDeniedException和AuthenticationException 。

FilterSecurityInterceptor负责权限校验的过滤器。

 

我们可以通过Debug查看当前系统中SpringSecurity过滤器链中有哪些过滤器及它们的顺序

下图是完整的认证流程

 

一、UsernamePasswordAuthenticationFilter

这里是把我们的用户名密码传进来,用UsernamePasswordAuthenticationToken接收,

紧接着封装为authentication 对象

 

 二、UserDetailsService

多层的过滤器,嵌套调用方法,来到UserDetailsService

 把前面的用户名密码也传到这里了

来到UserDetailsService,第一个方法就是loadUserByUsername, 这个方法就需要我们自己去重写,一般就是到数据库的用户表去查询用户(这里并没有验证密码是否正确), 然后匹配到用户的话就会来查询权限,返回一个UserDetails 对象;否则就抛出异常。

三、密码校验

默认的密码检验规则一般都不是我们想要的,所以这里我们要自己选中密码校验规则,

 

也就是我们编码流程的注入PasswordEncode Bean对象 到Java 工厂 ,return  这里就是选择密码校验规则,我们选择的是bcrypt密码比对器

四、返回authentication 对象

五、自定义JWT 拦截器

 

此拦截器对应代码中的

 

 

 

授权基本流程

在SpringSecurity中,会使用默认的拦截器FilterSecurityInterceptor来进行权限校验。

在FilterSecurityInterceptor中会从SecurityContextHolder获取其中的Authentication,然

后获取其中的权限信息,最后判断当前用户是否拥有访问当前资源所需的权限。

因此在项目中只需要把当前登录用户的权限信息也存入Authentication。

然后设置我们的资源所需要的权限即可。

在代码中我们是这样操作的

 

先查询到用户的权限信息,在通过LoginUser进行封装。传入permissions

 

我们最终是要把permissions中的权限,转换为SimpleGrantedAuthority类型并封装到authorities中。

在用户第一次获取权限时,此时authorities为空,就会走下面的流程

将permission转类型然后存入authorities中。再返回。

这样在FilterSecurityInterceptor中会从SecurityContextHolder获取其中的Authentication,然后就可以拿到其中的authorities权限信息。

 

SpringSecurity为我们提供了基于注解的权限控制方案,这也是项目中主要采用的方式。

可以使用注解去指定访问对应的资源所需的权限。但是要使用它我们需要先开启相关配置

开启配置需要再在SpringSecurity的配置类中增加如下注解:

 

然后就可以在接口上添加注解,表示此接口必须要有注解中的权限信息。

 

除了使用自带的权限校验方法,我们也可以自定义权限校验方法。

自己写一个,代替LoginUser中的hasAuthority。

SPEL表达式中使用 @my相当于获取容器中bean的名字为ex的对象。然后再调用这个对象的hasAuthority方法。

 

这样就可以运用到自己定义的权限校验方法。

我们也可以在配置类中使用使用配置的方式对资源进行权限控制,但是这种方式用的并不多。但是有些地方,比如图片,访问图片不能也要写个方法去拿权限吧?登录也不能要权限吧?因为未登录都还没有进行认证授权。所以我们需要对某些资源进行白名单放行。

 

在securityConfig中配置。

自定义失败处理

如果希望在认证失败或者是授权失败的情况下也能和我们的接口一样返回相同结构的json,这样可以让前端能对响应进行统一的处理。要实现这个功能我们需要知道SpringSecurity的异常处理机制。

在SpringSecurity中,在认证或者授权的过程中出现了异常会被ExceptionTranslationFilter捕获到。

在ExceptionTranslationFilter中会去判断是认证失败还是授权失败出现的异常。

如果是认证过程中出现的异常会被封装成AuthenticationException然后调用AuthenticationEntryPoint对象的方法去进行异常处理。

如果是授权过程中出现的异常会被封装成AccessDeniedException然后调用AccessDeniedHandler对象的方法去进行异常处理。

所以如果我们需要自定义异常处理,我们只需要自定义AuthenticationEntryPoint和AccessDeniedHandler然后配置给SpringSecurity。

这是代码中定义的授权失败处理器

 

这是代码中定义的认证失败处理器

 

 

 

跨域

在SpringSecurity项目中配置springboot跨域可能会有问题。

所以要通过SpringSecurity来进行跨域配置

 

posted on 2025-04-05 13:48  清清飞扬  阅读(1160)  评论(0)    收藏  举报