基于Security Oauth实现单点登录


前一段时间业务需要用到单点登录于是在网上看了各种博客,但是各种都是无头无尾的,最后只好看着官网一步步弄了一个,现在分享一些心得希望能帮到大家

注意 :这是一个example 为了追求精简,很多地方的写法非常不恰当 比如 直接new 对象这种方式在实际的开发中显然是不恰当,这篇文章需要有security的基础.如果没有请看我的上一篇文章.


基本概念

OAuth2.0 : 一种标准开放的认证协议,细致入微的描述请点击百度百科
认证服务器 : 也叫授权服务器,生成,校验用户信息 。
资源服务器 : 被保护的资源,具体的资源。例如订单服务就是一个资源服务,不可能让谁都能访问订单。

认证流程

认证模式一共有4种,这里写最常用的两种。

授权码模式 :举例拿 “使用QQ登录CSDN“,用户点击qq登录(发起资源请求)跳转到qq登录,输入正确的用户密码(输入正确),页面提示用户是否授权给CSDN,同意后 返回一个一次性临时的code,浏览器通过这个code去qq认证服务器申请token,校验成功后qq服务器返回token,CSDN服务器拿到token 获取用户信息,认证通过。特点: 安全系数高,token值不经过浏览器。
密码模式: 输入正确的用户信息,直接去认证服务器认证 申请token,然后通过token直接访问对应资源请求。特点:客户端会直接获取到你的token,有泄漏的风险。

mavn依赖

	<dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
	<dependency>
         <groupId>org.springframework.security.oauth.boot</groupId>
         <artifactId>spring-security-oauth2-autoconfigure</artifactId>
         <version>2.1.2.RELEASE</version>
    </dependency>
	<dependency>
         <groupId>org.springframework.security</groupId>
         <artifactId>spring-security-crypto</artifactId>
    </dependency>
    <dependency>
         <groupId>org.springframework.security</groupId>
         <artifactId>spring-security-core</artifactId>
         <version>5.3.3.RELEASE</version>
    </dependency>

认证服务器配置

配置自定义security的适配器

@Configuration
@EnableWebSecurity
@EnableAuthorizationServer //认证服务器
@EnableGlobalMethodSecurity(prePostEnabled=true) //开启权限注解
public class SpringWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
	
	//token仓库 
    @Bean
    public TokenStore tokenStore() {
        return new InMemoryTokenStore();
    }
	
	//密码处理
    @Bean
    public PasswordEncoder getPasswordEncoder() {
        PasswordEncoder passwordEncoder = new PasswordEncoder() {
            @Override
            public String encode(CharSequence charSequence) {
                return charSequence.toString();
            }

            @Override
            public boolean matches(CharSequence charSequence, String s) {
                return charSequence.equals(s);
            }
        };

        return passwordEncoder;
    }

    @Autowired
    private SpringUserDetailsService springUserDetailsService;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    	//自定义用户详情初始化 
    	//此处我省略这个类的编写,实在想看请看上一个博客
        auth.userDetailsService(springUserDetailsService).passwordEncoder(getPasswordEncoder());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
		//除去登录登出,其他请求一律认证
        http.authorizeRequests()
                .anyRequest().authenticated()
                .and().formLogin().permitAll()
                .and().logout().permitAll()
                .and().csrf().disable();
    }
    
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

}

配置类AuthorizationServerConfigurerAdapter(授权服务器适配器)

@Configuration
@EnableAuthorizationServer
class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {

    @Autowired
    AuthenticationManager authenticationManager;

    @Autowired
    ClientDetailsService clientDetailsService;

    private AuthorizationCodeServices authorizationCodeServices = new InMemoryAuthorizationCodeServices();

    private TokenStore tokenStore = new InMemoryTokenStore();

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        //配置客户端
        clients
                .inMemory() //存储策略 基于内存 (不建议使用)
                .withClient("client") //客户端ID
                .secret("123456")//客户端密钥
                .resourceIds("hi") //客户端具备那些资源服务器授权
                .authorizedGrantTypes("authorization_code","password","refresh_token") //可选的 认准方式
                .redirectUris("www.baidu.com") //使用授权码验证时,获取code跳转地址
                .scopes("read"); //授权的作用域  可看似为权限

//        clients.withClientDetails() 也可以设置你要放入的自定义ClientDetails 对象 功能和用法和 UserDetails 类似
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints
                .authenticationManager(authenticationManager)   //认证管理 这个是来自security适配器默认提供的
                .allowedTokenEndpointRequestMethods(HttpMethod.POST) //允许POST提交
                .authorizationCodeServices(authorizationCodeServices)  //授权码服务设置
                .tokenServices(getAuthorizationServerTokenServices()) //token服务 设置
        ;
    }

    //令牌管理服务
    @Bean
    public AuthorizationServerTokenServices getAuthorizationServerTokenServices(){
        DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
        defaultTokenServices.setClientDetailsService(clientDetailsService); // 配置客户端详情
        defaultTokenServices.setTokenStore(tokenStore); //设置存储方式
        defaultTokenServices.setSupportRefreshToken(true); // 支持刷新策略
        return defaultTokenServices;
    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
        //允许表单认证
        oauthServer
                .allowFormAuthenticationForClients() //允许POST请求
                .checkTokenAccess("permitAll()") //校验令牌合法性端点完全公开
                .tokenKeyAccess("permitAll()"); //公钥端点完全公开
    }

}

Oauth自带默认接口

授权服务器

  • /oauth/check_token 用于其他服务器远程校验token合合法性,以及获取token对应的用户信息
  • /oauth/token 申请token值,当grant_type 不同时,申请的参数也不一样.
  • /oauth/authorize 当使用授权码方式时,获取code

资源服务器配置

ResourceServerConfigurerAdapter 配置自定义的资源服务器适配器

@Configuration
@EnableResourceServer
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled=true)
public class AuthorizationConfig extends ResourceServerConfigurerAdapter {
    private final String RID= "hi"; //定义的资源服务器的id

    @Autowired
    RemoteTokenServices remoteTokenServices;

    @Override
    public void configure(ResourceServerSecurityConfigurer rsc){
        rsc
                .resourceId(RID) //资源服务器Id
                .tokenServices(remoteTokenServices) //认证token
                .tokenStore(new InMemoryTokenStore()) //存储策略
                .stateless(true);
    }

    @Override
    public void configure(HttpSecurity hs) throws Exception {
        hs.authorizeRequests()
                .anyRequest().authenticated()
                .and()
                .formLogin().loginPage("http://localhost:8080/login").permitAll();
    }

    @Bean
    public RemoteTokenServices checkToken(){
        RemoteTokenServices rts = new RemoteTokenServices(); //远程调用授权服务器的认证token 
        rts.setCheckTokenEndpointUrl("http://localhost:8080/oauth/check_token");
        rts.setClientId("client");
        rts.setClientSecret("123456");
//        DefaultTokenServices dts = new DefaultTokenServices(); //本地验证token
        return rts;
    }

}

测试

密码模式获取token:

在这里插入图片描述
client_id 和 client_secret 都要求在认证服务器有对应的客户端. 用户名和密码 只要能过UserDetails即可.要求就是必须能通过用户认证和客户端认证,且客户端还有对应资源服务器的id

grant_type :该参数用来声明是使用了那种参数来请求token 这里写的是password

授权码模式获取token

先得获取code,根据code获取token
请求获取code地址

http://localhost:8080/oauth/authorize?client_id=client&response_type=code

这个要填写 client_id 和 response_type 会给你跳转到登录页面
在这里插入图片描述
输入正确之后
在这里插入图片描述
获取code (这个自动跳转的页面我没有设置故404)

请求 /oauth/token 附带适配器里面的参数即可获取token
在这里插入图片描述

在这里插入图片描述

使用token

在这里插入图片描述
将返回结果中的 token_type + 空格 + access_token 填写成请求资源服务请求头中Authorization 的Value即可访问

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值