SpringSecurity+JWT实现认证和授权

171 阅读4分钟

“开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 7 天,点击查看活动详情

一、简介

Spring 是非常流行和成功的 Java 应用开发框架,Spring Security 正是 Spring 家族中的成员,Spring Security 基于 Spring 框架,提供了套Web 应用安全性的完整解决方案。

正如你可能知道的关于安全方面的两个主要区域是“认证“和“授权”(或者访问控制),一般来说,We 应用的安全性包括用户认证(Authentication)和用户授权 (Authorization) 两个部分,这两点也是 Spring Security 重要核心功能。

  • 用户认证指的是:验证某个用户是否为系统中的合法主体,也就是说用户能否访问该系统。用户认证一般要求用户提供用户名和密码。系统通过校验用户名和密码来完成认证过程。通俗点说就是系统认为用户是否能登录。

  • 用户授权指的是:验证某个用户是否有权限执行某个操作。在一个系统中,不同用户所具有的权限是不同的。比如对一个文件来说,有的用户只能进行读取,而有的用户可以进行修改。一般来说,系统会为不同的用户分配不同的角色,而每个角色则对应一系列的权限。通俗点讲就是系统判断用户是否有权限去做某些事情.

二、Spring Security过滤器

1.Spring Security中常见过滤器

  1. FilterSecuritylnterceptor: 是一个方法级的权限过滤器,基本位于过滤链的最底部。
  2. ExceptionTranslationFilter: 是个异常过滤器,用来处理在认证授权过程中抛出的异常。
  3. UseramePasswordAuthenticationFilter: 对/ogin 的 POST 请求做拦截,校验表单中用户

2.Spring Security过滤器链

Spring Security采用的是责任链的设计模式它有一条很长的过滤器链。

  • WebAsyncManagerlntegrationFilter: 将 Security 上下文与 Spring Web 中用于处理异步请求映射的WebAsyncManager 进行集成。
  • SecurityContextPersistenceFilter: 在每次请求处理之前将该请求相关的安全上下文信息加载到SecurityContextHolder 中,然后该次请求处理完成之后,将SecurityContextHolder 中关于这次请求的信息存储到一个仓”中,然后将SecurityContextHolder 中的信息清除,例如Session 中维护一个用户的安全信息就是这个过滤器处理的。
  • HeaderWriterFilter: 用于将头信息加入响应中。
  • CsrfFilter: 用于处理跨站请求伪造。
  • LogoutFilter: 用于处理退出登录。
  • UsemnamePasswordAuthenticationFilter: 用于处理其于表单的录请求,从表单中获取用户名和密码。默认情况下处理来自 /loain的请求。从表单中获取用户名和密码时,默认使用的表单 name 值为 usemame 和 password,这两个值可以通过设置这个过滤器的usernameParameter 和 passwordParameter 两个参数的值进行修改。
  • DefaulLoginPageGeneratingFilter: 如果没有配置登录页面,那系统初始化时就会配置个过滤器,并且用于在需要进行登录时生成一个登录表单页面。
  • BasicAuthenticationFilter: 检测和处理 http basic 认证。
  • RequestCacheAwareFilter: 用来处理请求的缓存。
  • SecurityContextHolderAwareRequestFilter: 主要是包装请求对象 request。
  • AnonymousAuthenticationFilter: 检测 SecurityContextHolder 中是否存在Authentication 对象,如果不存在为其提供一个匿名Authentication。
  • SessionManagementFilter: 管理 session 的过滤器。
  • ExceptionTranslationFilter: 处理 AccessDeniedException 和AuthenticationException 异常。
  • FilterSecuritylnterceptor: 可以看做过滤器链的出口。
  • RememberMeAuthenticationFilter: 当用户没有登录而直接访河资源时,从 cookie里找出用户的信息,如果 Spring Security 能够识别出用户提供的 remember me cookie,用户将不必填写用户名和密码,而是直接登录进入系统,该过滤器默认不开启。

三、springboot整合Spring Security

1.pom.xml

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.5.0</version>
</parent>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <!--SpringSecurity依赖-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <!--fastjson依赖-->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.83</version>
    </dependency>
    <!--jwt依赖-->
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt</artifactId>
        <version>0.9.0</version>
    </dependency>
    <!--MybatisPlus依赖-->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.4.1</version>
    </dependency>
    <!--mysql驱动-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
    <!--junit依赖-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-pool2</artifactId>
    </dependency>
    <!-- Jedis依赖 -->
    <dependency>
        <groupId>redis.clients</groupId>
        <artifactId>jedis</artifactId>
        <version>3.6.0</version>
    </dependency>
</dependencies>

2.application.yml

spring:
  datasource:
    url: jdbc:mysql://ip/security?characterEncoding=utf-8&serverTimezone=UTC
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver

  # redis
  redis:
    host: ip
    port: 6379
    timeout: PT10S
#    lettuce:
#      pool:
#        max-active: 100
#        max-wait: PT10S
#        max-idle: 30
#        min-idle: 1
#    # password: root
    pool:
      minIdle: 1
      maxIdle: 10
      maxWait: 3
      maxActive: 8
    password: root

3.securityConfig

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    public PasswordEncoder passwordEncoder() {
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        String encode = encoder.encode("1234");
        System.out.println("------------" + encode + "-----------");
        return encoder;
    }

    @Autowired
    JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
    @Autowired
    AuthenticationEntryPoint authenticationEntryPoint;
    @Autowired
    AccessDeniedHandler accessDeniedHandler;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                //关闭csrf
                .csrf().disable()
                //不通过Session获取SecurityContext
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                // 对于登录接口 允许匿名访问
                .antMatchers("/user/login").anonymous()
                // 除上面外的所有请求全部需要鉴权认证
                .anyRequest().authenticated();

        //把token校验过滤器添加到过滤器链中
        http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);

        //配置异常处理器
        http.exceptionHandling()
                //配置认证失败处理器
                .authenticationEntryPoint(authenticationEntryPoint)
                .accessDeniedHandler(accessDeniedHandler);

        //允许跨域
        http.cors();
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
}

...中间省略user创建usercontroller使用等

4.登录成功后返回token

image.png

5.token过期或者伪造token返回自定义异常拦截器返回

image.png