基于SpringSecurityOAuth2实现单点登录, 简单示例用于学习SpringSecurityOAuth2. 解决了遇到的所有SpringSecurityOAuth2的坑

概述

基于SpringSecurityOAuth2实现单点登录, 简单示例用于学习SpringSecurityOAuth2. 解决了遇到的所有SpringSecurityOAuth2的坑。添加了注销登录,扩展了短信登录, 验证码等功能,实现了Vue前后端分离情况下的OAuth2单点登录。 麻雀虽小五脏俱全,满足企业级应用.

详细

一、需求(要做什么)

1.基于SpringSecurityOAuth2实现单点登录,实现基于SpringSecurityOAuth2的客户端对接认证中心, 实现基于OAuth2协议的原始方法对接认证中心, 基于vue前后端分离的方式对接认证中心。

2.编写基于oauth2认证的4种认证模式的demo, 让初学者一看就懂。 

3.实现登陆,登录通过账户密码登录, 也可通过手机验证码登录, 实现记住我,十天免登陆功能。

4.实现注销登录。

二、理论概述

        OAuth(开放授权)是一个开放标准,允许用户授权第三方应用访问他们存储在另外的服务提供者上的信息,而不需要将用户名和密码提供给第三方移动应用或分享他们数据的所有内容。 这是一个标准, spring也推荐也默认采用这种登录授权的模式, 所以跟着spring来,方向不会错。

        OAuth 的核心就是向第三方应用颁发令牌,资源服务器可以向客户端颁发令牌。客户端通过令牌,去请求数据。

        OAuth 2.0 规定了四种获得令牌的流程:

            authorization code(授权码模式)

            implicit(简化模式)

            resource owner password credentials(密码模式)

            client credentials(客户端模式)

        授权码模式是最常用的流程,安全性也最高,它适用于那些有后端的 Web 应用, 目前阿里,腾讯等大型互联网公司,甚至一些政府项目等都用这种方式, 看来不学不行了。

        

三、代码分析

1.首先引入pom

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
 
<dependency>
    <groupId>org.springframework.security.oauth</groupId>
    <artifactId>spring-security-oauth2</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.security.oauth.boot</groupId>
    <artifactId>spring-security-oauth2-autoconfigure</artifactId>
</dependency>
 
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-jwt</artifactId>
</dependency>
<dependency>
    <groupId>com.nimbusds</groupId>
    <artifactId>nimbus-jose-jwt</artifactId>
</dependency>

2.  认证中心的核心代码

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
     
     
    @Autowired
    private DataSource dataSource;
     
    @Bean
    public PersistentTokenRepository persistentTokenRepository() {
        JdbcTokenRepositoryImpl  tokenRepository=new JdbcTokenRepositoryImpl();
        tokenRepository.setDataSource(dataSource);
        //第一次创建开启
        //tokenRepository.setCreateTableOnStartup(true); 
        return tokenRepository;
    }
     
    @Autowired
    SsoUserDetailsServiceImpl  myUserDetailsService;
     
    @Autowired
    SsoSavedRequestAwareAuthenticationSuccessHandler  myOwnSavedRequestAwareAuthenticationSuccessHandler;
     
    @Autowired 
    SsoAuthenticationFailureHandler  myAuthenticationFailureHandler;
     
    @Autowired 
    SsoLogoutSuccessHandler  myLogoutSuccessHandler;
     
    @Autowired
    ValidateCodeFilterSecurityConfiguration  validateCodeFilterSecurityConfiguration;
     
    @Autowired
    LoginFormSecurityConfiguration  loginFormSecurityConfiguration;
     
 
    @Override
    protected void configure(HttpSecurity http) throws Exception {
 
         //跨站请求伪造
        http.csrf().disable();
         //解决iframe问题
        http.headers().frameOptions().disable();
            
                http
                .apply(validateCodeFilterSecurityConfiguration)
                .and()
               // .addFilterBefore(parametersFilter, UsernamePasswordAuthenticationFilter.class)
              //表单登录
                .formLogin()   
                    //自定义登陆的页面
                    .loginPage("/authentication/require")   
                    //指定登陆action 的 url
                    .loginProcessingUrl("/authentication/form")  
                    //登录成功处理
                    .successHandler(myOwnSavedRequestAwareAuthenticationSuccessHandler)  
                    //登出失败处理
                    .failureHandler(myAuthenticationFailureHandler)  
                    .and()
                    .rememberMe()
                    .tokenRepository(persistentTokenRepository())
                    .tokenValiditySeconds(3600)
                    .userDetailsService(myUserDetailsService)
                    .and()
                    //请求认证模块   所有请求都需要认证 
                    .authorizeRequests()     
                    .antMatchers("/authentication/require",
                            "/authentication/form",
                            "/logout",
                            "/login",
                            "/login/time",
                            "/getBackPasswordIndex",
                            "/register",
                            "/code/image",
                            "/code/sms",
                            "/error",
                            "/oauth/exit",
                            "/oauth/authorize/**",
                            "/oauth/token/**",
                            "/auth/oauth/token/**",
                            "/oauth/check_token/**",
                            "/oauth/confirm_access/**",
                            "/oauth/error/**",
                            "/images/**", 
                            "/plugin/**", 
                            "/js/**", 
                            "/css/**").permitAll() 
                   //所有请求认证
                    .anyRequest().authenticated()  
                    .and()
                    .logout()
                    .logoutUrl("/logout")
                    .logoutSuccessHandler(myLogoutSuccessHandler)
                    .and()
                    .apply(loginFormSecurityConfiguration); 
                     
    }
     
 
 
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
     
     
    /**
     * session过期
     * @return
     */
    @Bean
    public HttpSessionEventPublisher httpSessionEventPublisher() {
        return new HttpSessionEventPublisher();
    }
}
 /**
     * DefaultTokenServices 实现了ConsumerTokenServices接口, revokeToken方法定义了删除token的方法
     */
    @Autowired
    @Qualifier("consumerTokenServices")
    private ConsumerTokenServices consumerTokenServices;
 
     
    @RequestMapping("/remove/token")
    public void removeToken(HttpServletRequest request, HttpServletResponse response) throws IOException {
        String tokenValue = request.getParameter("access_token");
        String redirectUri = request.getParameter("redirect_uri");
        consumerTokenServices.revokeToken(tokenValue);
         
        // 清除记住我的数据
        request.getSession().invalidate();
         
        response.sendRedirect(redirectUri);
    }

3. 客户端的核心代码

@Configuration
@EnableOAuth2Sso
public class SecurityConfig extends WebSecurityConfigurerAdapter {
     
    @Autowired
    MyLogoutSuccessHandler  myLogoutSuccessHandler;
     
    @Override
    protected void configure(HttpSecurity http) throws Exception {
         
         //跨站请求伪造
        http.csrf().disable();
         //解决iframe问题
        http.headers().frameOptions().disable();
 
        http.antMatcher("/**")
                .authorizeRequests()
                .antMatchers("/login**","/logout/**","/js/**","/outer/**").permitAll()
                .anyRequest()
                .authenticated()
                .and()
                .logout()
                .logoutUrl("/logout")
                .logoutSuccessHandler(myLogoutSuccessHandler);
 
        super.configure(http);
    }
     
    /**
     * session过期
     * @return
     */
    @Bean
    public HttpSessionEventPublisher httpSessionEventPublisher() {
        return new HttpSessionEventPublisher();
    }
 
 
}
四、项目文件结构截图

image.png

五、启动部署流程

1. 首先 在mysql创建数据库sso-oauth,在数据库执行sqlj脚本: sso-oauth-parent/sql/sso-oauth.sql

2. 然后用idea导入maven项目sso-oauth-parent, 待pom依赖jar包下载完成后执行下一步。

3. 配置redis

解压 so-oauth-parent\Redis-x64-3.0.504.zip 

双击 redis-server.exe   启动redis

image.png

image.png

4. 启动认证中心 , 启动client1, client2, client3 

image.png

其余client项目类似

4. 编译启动vue-demo( 如果没有前后端分离vue认证需求, 可以忽略本步。

要想启动vue项目,  首先安装nodejs环境 和vue环境,  比较简单参考百度

接下来进入:  sso-oauth-parent\vue-demo 目录, 执行cmd命令

npm install   # 安装vue依赖

npm install jquery   #安装jquery插件

npm run serve 启动服务

成功启动看到如下

image.png

六、演示效果

认证中心登录界面:

image.png

auth-center主页

image.png

无登录跳转client1主页

image.png

无登录跳转client2主页

image.png

无登录跳转client3-vue主页

image.png

退出登录

image.png

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

西安未央

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

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

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

打赏作者

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

抵扣说明:

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

余额充值