Spring Security 整合 JWT

SpringSecurity整合JWT

1.认证思路分析

SpringSecurity主要是通过过滤器来实现功能的!我们要找到SpringSecurity实现认证和校验身份的过滤器!

回顾集中式认证流程

用户认证: 使用UsernamePasswordAuthenticationFilter 过滤器中attemptAuthentication方法实现认证功能,该过滤器父类中successfulAuthentication方法实现认证成功后的操作。

身份校验: 使用 BasicAuthenticationFilter 过滤器中doFilterInternal方法验证是否登录,以决定能否进入后续过滤器。

2.分析分布式认证流程

用户认证:
由于是前后端分离,项目架构设计是分布式的,我们要满足可以接受异步post的认证请求参数,需要修改UsernamePasswordAuthenticationFilter过滤器中attemptAuthentication方法,让其能够接收请求体。 另外,默认successfulAuthentication方法在认证通过后,是把用户信息直接放入session就完事了,现在我们需要修改这个方法,在认证通过后生成token并返回给用户。

身份校验

BasicAuthenticationFilter

使用BasicAuthenticationFilter 过滤器中 doFilterInternal方法 校验用户是否登录,就是看session中是否有用户信息,我们要修改为,验证用户携带的token是否合法,并解析出用户信息,交给SpringSecurity,以便于后续的授权功能可以正常使用。

权限框架SpringSecurity 前后端分离登录 (BasicAuthenticationFilter)
https://blog.csdn.net/zxd1435513775/article/details/124801891

// 自定义AuthenticationManager
public class JwtAuthenticationFilter extends BasicAuthenticationFilter {
}

认证管理器,负责验证,认证成功后,AuthenticationManager 返回一个填充了用户认证信息(包括权限信息、身份信息、详细信息等,但密码通常会被移除)的 Authentication 实例。
然后再将 Authentication 设置到 SecurityContextHolder 容器中。
AuthenticationManager 接口是认证相关核心接口,也是发起认证的入口。但它一般不直接认证,其常用实现类 ProviderManager 内部会维护一个List<AuthenticationProvider> 列表,存放多种认证方式,默认情况下只需要通过一个 AuthenticationProvider 的认证,就可以认为是登录成功。

UserDetailsService
loadUserByUsername

加载登录账户 loadUserByUsername
https://blog.csdn.net/u012811805/article/details/118401750

AuthenticationManager 的 authentication 过程

链接:https://blog.csdn.net/wwang_dev/article/details/119107355

// 执行登录认证过程
Authentication authentication = authenticationManager.authenticate(token);
// 认证成功存储认证信息到上下文
SecurityContextHolder.getContext().setAuthentication(authentication);
// 生成令牌并返回给客户端
String genToken = JwtTokenUtils.generateToken(authentication);
token.setToken(genToken);
// 保存权限到redis
redisTemplate.opsForValue().set(genToken, JSONArray.toJSONString(authentication.getAuthorities()));
return token;

从这里也可以看出为什么 pinciple 和 credentials 的类型都是 Object,因为 pinciple 认证前需要传 String 类型的 username,认证后需要传实现了UserDetails 接口的用户对象;而 credentials 认证前需要传前端传来的String类型的password,认证后需要传 String类型的密码 或者 null。

通常都是,接收 前端传来的 username 和 password,调用 两个参数的构造器,创建一个未认证的对象,交给AuthenticationManager进行认证。认证成功后,调用三个参数的构造器,创建一个已认证的对象。

@Order(10)
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig2 extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

    //认证服务器 配置用户认证 // 使用自定义身份验证组件  //指定密码加密策略
    @Override
     public void configure(AuthenticationManagerBuilder auth) throws Exception {
         // auth.userDetailsService(userDetailsService);
        auth.authenticationProvider(new JwtAuthenticationProvider(userDetailsService));
    }
}
9 自定义获取数据库用户权限实现

改造 WebSecurityConfiguration 加载自定义UserDetailsService

9.1 UserDetailsService实现

根据用户名获取用户的权限,链接:https://blog.csdn.net/tiancxz/article/details/108856337

//返回一个自定义数据库获取用户权限的实现
    @Autowired
    public  UserDetailsServiceImpl userDetailsService;
     
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
          auth.userDetailsService(userDetailsService);

每次获取token时,都会进入自定义实现从数据库获取权限
http://client:secret@localhost:10101/oauth/token
There is no PasswordEncoder mapped for the id “null”

DaoAuthenticationProvider 身份验证提供者

史上最简单的Spring Security教程(二十六):DaoAuthenticationProvider详解
https://blog.csdn.net/liuminglei1987/article/details/108279836

说到 Authentication 相信都不陌生。最著名的,便是 UsernamePasswordAuthenticationToken。
而 DaoAuthenticationProvider 是用于解析并认证 UsernamePasswordAuthenticationToken 的这样一个认证服务提供者。

其最终目的,就是根据 UsernamePasswordAuthenticationToken,获取到 username,然后调用 UserDetailsService 检索用户详细信息。

在其基类 AbstractUserDetailsAuthenticationProvider 中,需要子类实现 retrieveUser() 、additionalAuthenticationChecks() 等抽象方法。
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

A系统(单点登录客户端)首次访问受保护的资源触发单点登录流程说明

1、用户通过浏览器访问A系统被保护的资源链接
2、A系统判断当前会话是否登录,如果没有登录则跳转到A系统登录地址/login
3、A系统首次接收到/login请求时没有state和code参数,此时A系统拼接系统配置的单点登录服务器授权url,并重定向至授权链接。
4、单点登录服务器判断此会话是否登录,如果没有登录,那么返回单点登录服务器的登录页面。
5、用户在登录页面填写用户名、密码等信息执行登录操作。
6、单点登录服务器校验用户名、密码并将登录信息设置到上下文会话中。
7、单点登录服务器重定向到A系统的/login链接,此时链接带有code和state参数。
8、A系统再次接收到/login请求,此请求携带state和code参数,系统A通过OAuth2RestTemplate请求单点登录服务端/oauth/token接口获取token。
9、A系统获取到token后,首先会对token进行解析,并使用配置的公钥对token进行校验(非对称加密),如果校验通过,则将token设置到上下文,下次访问请求时直接从上下文中获取。
10、A系统处理完上下问会话之后重定向到登录前请求的受保护资源链接。

B系统(单点登录客户端)访问受保护的资源流程说明

1、用户通过浏览器访问B系统被保护的资源链接
2、B系统判断当前会话是否登录,如果没有登录则跳转到B系统登录地址/login
3、B系统首次接收到/login请求时没有state和code参数,此时B系统拼接系统配置的单点登录服务器授权url,并重定向至授权链接。
4、单点登录服务器判断此会话是否登录,因上面访问A系统时登陆过,所以此时不会再返回登录界面。
5、单点登录服务器重定向到B系统的/login链接,此时链接带有code和state参数。
6、B系统再次接收到/login请求,此请求携带state和code参数,系统B通过OAuth2RestTemplate请求单点登录服务端/oauth/token接口获取token。
7、B系统获取到token后,首先会对token进行解析,并使用配置的公钥对token进行校验(非对称加密),如果校验通过,则将token设置到上下文,下次访问请求时直接从上下文中获取。
8、B系统处理完上下问会话之后重定向到登录前请求的受保护资源链接。

http.formLogin()

loginProcessingUrl

loginProcessingUrl(“/login/doLogin”)//表单提交路劲,提交后执行认证逻辑
loginProcessingUrl的作用是用来拦截前端页面对/login/doLogin这个的请求的,拦截到了就走它自己的处理流程,导致我们自己后端写的/login/doLogin这个接口有写跟没写是一样的。


successHandler

**(重点)**由于未在WebSecurity配置中设置successHandler,因此默认情况下将调用SavedRequestAwareAuthenticationSuccessHandler。

ExceptionTranslationFilter在认证开始前把request缓存到session中,当认证成功后,在SavedRequestAwareAuthenticationSuccessHandler里取出缓存的request,跳转回认证前用户想访问的url


AuthenticationSuccessHandler接口(登录成功之后的处理逻辑)

异常处理

SpringSecurity(二十):异常处理 https://www.cnblogs.com/wangstudyblog/p/14823512.html

http.exceptionHandling().authenticationEntryPoint()
http.exceptionHandling().authenticationEntryPoint(new CustomAuthenticationExceptionEntryPoint())
Spring Security 执行登录认证过程

在这里插入图片描述

图 https://blog.csdn.net/weixin_64742764/article/details/124813433

参考地址

Spring Security + JWT 实现单点登录
https://blog.csdn.net/xiyang_1990/article/details/124487755
https://blog.csdn.net/CSDN_KONGlX/article/details/125486825


https://blog.csdn.net/qq_18671415/article/details/118518450
http://t.zoukankan.com/7788IT-p-10693154.html
https://blog.csdn.net/pxg943055021/article/details/124752669


https://blog.csdn.net/wang121213145/article/details/124850518
https://blog.csdn.net/asd747571569/article/details/122511434

OAuth 2.0实现分布式认证授权-认证服务器配置 2
https://blog.csdn.net/u011066470/article/details/119974904

Spring Security
https://blog.csdn.net/m0_64714024/article/details/125549592



OAuth2.0认证和授权原理(示例代码)
https://www.136.la/tech/show-45950.html

Spring Security Oauth2 常用授权方式配置详细教程(一)
https://blog.csdn.net/tiancxz/article/details/108856337

史上最简单的Spring Security教程(二十六):DaoAuthenticationProvider详解
https://blog.csdn.net/liuminglei1987/article/details/108279836

认证成功后回跳

Spring Security认证成功后回跳
https://blog.csdn.net/gangsijay888/article/details/81171647/

SpringSecurity保存登录前的请求页面和跳转回登录前页面的源码分析
https://blog.csdn.net/HHoao/article/details/124559410

SpringSecurity登录后重定向到登录前访问页面策略 https://blog.csdn.net/ainizfb/article/details/123221490
基于SpringSecurity OAuth2实现单点登录——集成Github实现第三方账户登录
https://blog.csdn.net/hou_ge/article/details/120455557
其他

There is no PasswordEncoder mapped for the id "null"的解决办法
https://www.cnblogs.com/jpfss/p/11004828.html

ajax
https://wenku.baidu.com/view/917ac6e0a900b52acfc789eb172ded630b1c988c.html

springsecurity oauth2使用jwt实现单点登录(文章目录)
https://blog.csdn.net/weixin_44837750/article/details/125767591

自定义权限路径配置
https://blog.csdn.net/weixin_50025568/article/details/123548098

测试页 备份

@Order(10)
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig3 extends WebSecurityConfigurerAdapter {

    @Override //SuperAdmin Admin
    public void configure(AuthenticationManagerBuilder auth) throws Exception{
        // 使用自定义身份验证组件,指定密码加密策略
        //auth.inMemoryAuthentication().withUser("admin").password("123456").roles("Admin").and().passwordEncoder(passwordEncoder());
        
        //auth.userDetailsService(userDetailsService);
        
        auth.inMemoryAuthentication().withUser("admin").password(passwordEncoder().encode("123456")).roles("Admin");
    }

    @Bean
    public static PasswordEncoder passwordEncoder(){
        // return NoOpPasswordEncoder.getInstance();
        return new BCryptPasswordEncoder();
    }

    //form表单重写
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin()
                //.loginPage("/login.html")
                //.defaultSuccessUrl("/index_defaultSuccessUrl.html")
                //.loginProcessingUrl("/login")//表单提交路劲,提交后执行认证逻辑
                //.successForwardUrl("/index.html")
                //.successHandler((request, response, auth) -> { System.out.println(" doLogin"); })
                //
                .successHandler(new AuthenticationSuccessHandler() {
                    @Override
                    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
                        response.setContentType("application/json;charset=utf-8");
                        RequestCache cache = new HttpSessionRequestCache();
                        SavedRequest savedRequest = cache.getRequest(request, response);
                        String url = savedRequest.getRedirectUrl();
                        System.out.println("getRedirectUrl: " + url);
                        response.sendRedirect(url);
                    }
                })
                .failureForwardUrl("/fail.html"); // 登录失败后的路劲

        //
        http.authorizeRequests()
                .antMatchers("/sso/oauth/*").permitAll()//
                .antMatchers("/login.html").permitAll()//登录页不需要认证
                .antMatchers("/login").permitAll()
                .antMatchers("/**/*.js", "/**/*.css").permitAll()
                .anyRequest().authenticated()
                .and()
                .csrf().disable();
    }


    @Bean
    @Override
    public AuthenticationManager authenticationManager() throws Exception {
    	return super.authenticationManager();
    }
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值