基于Spring Security 和JWT实现单点登录

  • 引入Spring Security依赖

  • 当我们引入Spring Security框架之后,默认所有请求都得通过认证,没有通过的话会被重定向到内置的登录页面,登录成功就会自动重定向到此前访问的页面,但是所有的POST请求是不允许访问的,GET可以。

    我们需要创建一个配置类,继承WebSecurityConfigurerAdapter类,重写其中的方法:

    他默认会调用父类的一个super.configure(http)方法,注释掉之后,所有的请求不需要通过认证也可以直接访问了

    @Override
    protected void configure(HttpSecurity http) throws Exception {
       // 如果不调用父类方法,默认所有请求都不需要通过认证,可以直接访问
       // super.configure(http);
    
  • 还需要定义一个没有通过认证也能访问的url数组,匹配的路径不需要认证也可以访问(例如登录请求就需要放行)

      // 需要放行的请求
        String[] urls = {
                "/login",
                "/**"
        };
    
    
  • POST请求在登陆成功之后不能访问,是因为在Spring Security框架中,默认开启了防止伪造的跨域攻击的机制

    其基本做法就是在POST请求中,要求客户端提交其随机生成的一个UUID值,例如,(在没有禁用防止伪造跨域攻击时)在Spring Security的登录页面中有:

    <input name="_csrf" type="hidden" value="b6dc65f8-e0cf-4907-bdaf-a5f19b759f93" />
    

    以上代码中的value值就是一个UUID值,是前次GET请求时由服务器端响应的,服务器端会要求客户端携带此UUID来访问,否则,就会将请求视为伪造的跨域攻击行为

    我们需要在继承WebSecurityConfigurerAdapter类中将防止伪造跨域攻击的机制禁用http.csrf().disable(),POST请求就可以访问

     // 将防止伪造跨域攻击的机制禁用
        http.csrf().disable();
    
  • 在Spring Security处理认证时,还会自动装配Spring容器中的密码编码器,所以还需要在配置类中添加密码编码器:

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    
    
  • 要实现前后端分离的登录模式,需要使用控制器接收来自客户端的登录请求,然后在Service调用AuthenticationManagerauthenticate()方法处理认证,所以需要在配置类中添加一个认证管理器并添加到Spring容器中供Service调用。

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
    
    
  • Spring Security默认提供了用户名user和启动时随时生成的UUID密码,如果要自定义账号密码,需要实现UserDetailsService接口,重写接口中的方法,Spring Security框架在处理认证时,会自动根据提交的用户名(用户在登录表单中输入的用户名)来调用重写的方法:

    UserDetails loadUserByUsername(String username);

    以上方法应该返回匹配的用户详情(UserDetails类型的对象),接下来,Spring Security会自动根据用户详情(UserDetails对象)来完成认证过程,例如判断密码是否正确等。

    当执行认证时会自动调用该方法,然后在方法中可以通过参数(用户输入的用户名)来查数据库(如果查询结果为空,证明该用户不存在,抛出异常由全局异常处理器来处理),将查询结果中所需的账号、密码、权限赋值到UserDetails类型的对象中返回,此对象中的账号、密码以及权限,就是自定义的账号密码,也就是后面 Spring Security判断登录的依据。
  • 当客户端发送求登录时,在Service中执行认证,当认证成功后时会返回Authentication接口类型的对象,此类型中的principal就是通过认证的用户信息,也就是之前自定义账号密码时重写的方法的返回值。然后需要从principal中取出当前用户的账号、密码以及权限来生成JWT,然后返回给客户端。在此之后客户端访问服务端都需要携带登录成功时返回的JWT。

生成JWT代码:

  • Spring Security框架是依据Security上下文(SecurityContext)中的认证信息来判定当前用户是否通过认证以及判断用户的权限,所以当客户端携带JWT发送请求时,需要在控制器之前解析JWT、创建认证对象、然后将认证对象保存到security上下文中,因此需要创建一个过滤器来处理。

  • 并且,此过滤器还需在Spring Security的过滤器之前执行,还应该在SecurityConfiguration中添加此过滤器,并将过滤器添加到Spring Security框架的过滤器链中

    @Autowired
    private JwtAuthorizationFilter jwtAuthorizationFilter;
    
    // 将JWT过滤器添加到Spring Security框架的过滤器链中
    http.addFilterBefore(jwtAuthorizationFilter, UsernamePasswordAuthenticationFilter.class);
    
  • 需要先在配置类(强烈建议SecurityConfiguration)上添加@EnableGlobalMethodSecurity(prePostEnabled = true)注解,以启用方法级别的权限检查!

    需要注意:当客户端的异步请求定义了请求头中的Authorization时,在服务器端,在SecurityConfiguration类的void configurer(HttpSecurity http)方法中,需要添加以下配置:

    http.cors();
    

    否则客户端将出现跨域错误!

  • 当前后端需要跨域访问

    在默认情况下,是不允许发送跨域访问请求的.
    当服务器端允许来自跨域的客户端发送请求时,在Spring Boot项目中,需要使用配置类实现WebMvcConfigurer接口,并重写addCorsMappings()方法进行配置.

    public void addCorsMappings(CorsRegistry registry) {
            registry.addMapping("/**")
                    .allowedOriginPatterns("*")
                    .allowedHeaders("*")
                    .allowedMethods("*")
                    .allowCredentials(true)
                    .maxAge(3600);
    

完整配置类代码:



/**
 * Spring Security配置类
 *
 * @author Hyde
 * @version 0.0.1
 */
@Slf4j
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

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

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 如果不调用父类方法,默认所有请求都不需要通过认证,可以直接访问
        // super.configure(http);

        // 白名单
        String[] urls = {
                "/favicon.ico",
                "/doc.html",
                "/**/*.js",
                "/**/*.css",
                "/swagger-resources",
                "/v2/api-docs",
                "/admins/login"
        };

        // 启用CorsFilter(Spring Security内置的处理跨域的过滤器)
        http.cors();

        // 将防止伪造跨域攻击的机制禁用
        http.csrf().disable();

        // 提示:关于请求路径的配置,如果同一路径对应多项配置规则,以第1次配置的为准
        http.authorizeRequests() // 管理请求授权

                // .mvcMatchers(HttpMethod.OPTIONS, "/**")
                // .permitAll()

                .mvcMatchers(urls) // 匹配某些路径
                .permitAll() // 直接许可,即可不需要通过认证即可访问
                .anyRequest() // 除了以上配置过的以外的其它所有请求
                .authenticated(); // 要求是“已经通过认证的”

        // 将JWT过滤器添加到Spring Security框架的过滤器链中
        http.addFilterBefore(jwtAuthorizationFilter, UsernamePasswordAuthenticationFilter.class);

        // 启用登录表单
        // 当未认证时:
        // -- 如果启用了表单,会自动重定向到登录表单
        // -- 如果未启用表单,则会提示403错误
        // http.formLogin();
    }

    @Autowired
    private JwtAuthorizationFilter jwtAuthorizationFilter;

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
针对你的问题,我可以提供以下的一些思路: 1. 首先,你需要使用Spring Security实现用户认证和授权。在Spring Security中,你可以配置多个AuthenticationProvider实现不同的认证方式。具体而言,你需要实现一个自定义的AuthenticationProvider,用于验证JWT令牌是否有效。在进行认证时,该AuthenticationProvider会获取到请求头中的JWT令牌,然后进行解析和验证,如果解析和验证成功,就会把该用户的信息构建成一个Authentication对象返回给Spring Security框架。 2. 其次,你需要使用JWT来生成令牌并将其发送给客户端。JWT(Json Web Token)是一种轻量级的身份认证机制,可以在不依赖于Session的情况下实现用户的身份认证和授权。在使用JWT时,你需要创建一个私钥和公钥对,私钥用于对JWT进行签名,公钥用于对JWT进行验证。当用户登录成功后,你需要使用私钥对用户的信息进行签名,生成一个JWT令牌,并将该令牌发送给客户端。客户端在以后的请求中需要将该令牌作为Authorization请求头的值发送给服务端。 3. 最后,你需要在服务端验证JWT令牌的有效性。在服务端接收到请求后,你需要从请求头中获取JWT令牌,并使用公钥对该令牌进行验证,验证成功后就可以获得该用户的信息。在接下来的业务逻辑中,你可以使用该用户的信息来进行权限判断等操作。 希望以上的思路对你有所帮助。如果你需要更加详细的实现步骤,可以参考Spring SecurityJWT相关的官方文档。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hyde_jn

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值