基于Spring Security OAuth2.0实现单点登录SSO
1. 概述
本文简要总结一下如果使用Spring Security OAuth和Spring Boot来实现SSO,文末有样例代码。不了解OAuth2.0协议的同学请参考《OAuth2.0协议原理详解》
整个工程包括三个独立的应用,一个认证服务和两个客户端应用,结构非常简单。当一个用户访问客户端应用中被防护的API时,系统会被自动重定向到认证服务,之后我们使用OAuth2.0的Authorization code授权方式来实现认证授权。
2. 客户端APP
我们先从客户端应用入手,使用Spring Boot可以最大程度简化配置
2.1 Maven依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <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.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.thymeleaf.extras</groupId> <artifactId>thymeleaf-extras-springsecurity4</artifactId> </dependency>
2.2 安全配置
客户端的安全配置如下:
@Configuration @EnableOAuth2Sso public class UiSecurityConfig extends WebSecurityConfigurerAdapter { @Override public void configure(HttpSecurity http) throws Exception { http.antMatcher("/**") .authorizeRequests() .antMatchers("/", "/login**") .permitAll() .anyRequest() .authenticated(); } }
配置最核心的部分是 @EnableOAuth2Sso注解来开启SSO。这里要注意,我们需要重写WebSecurityConfigurerAdapter 否则所有的路径都会受到SSO的保护,这样无论用户访问哪个页面都会被重定向到登录页面,在这个例子里,index和login页面是唯一不需要被防护的。
最后,我们定义一个RequestContextListener bean来处理request scopes。
application.yml: server: port: 8082 context-path: /ui session: cookie: name: UISESSION security: basic: enabled: false oauth2: client: clientId: SampleClientId clientSecret: secret accessTokenUri: http://localhost:8081/auth/oauth/token userAuthorizationUri: http://localhost:8081/auth/oauth/authorize resource: userInfoUri: http://localhost:8081/auth/user/me spring: thymeleaf: cache: false
说明:
我们禁用了default Basic Authentication
accessTokenUri 是获取访问令牌的URL
userAuthorizationUri是授权用户被重定向的目标URL
userInfoUri是用户终端访问用户信息的URL
在这个case里,认证服务是我们自己建设的,但是在实际的应用场景下认证服务往往是第三方提供的比如Facebook 或者 gitHub
2.3 前端
前端并非本文的重点,这里简单提一下。客户端有一个非常简单的前端页面
index.html:
<h1>Spring Security SSO</h1> <a href="securedPage">Login</a>
securedPage.html:
<h1>Secured Page</h1> Welcome, <span th:text="${#authentication.name}">Name</span>
securedPage.html需要用户通过授权才能访问,如果一个未授权的用户访问这个页面,他会被重定向到login页面
3. 认证服务器
3.1 Maven依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.security.oauth</groupId> <artifactId>spring-security-oauth2</artifactId> </dependency>
3.2 OAuth配置
本例中我们把AS(认证服务)器和RS放(资源服务器)在一个实例中部署。
RS配置如下:
@EnableResourceServer public class AuthorizationServerApplication extends SpringBootServletInitializer { public static void main(String[] args) { SpringApplication.run(AuthorizationServerApplication.class, args); } }
AS配置如下:
@Configuration @EnableAuthorizationServer public class AuthServerConfig extends AuthorizationServerConfigurerAdapter { @Autowired private AuthenticationManager authenticationManager; @Override public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception { oauthServer.tokenKeyAccess("permitAll()") .checkTokenAccess("isAuthenticated()"); } @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory() .withClient("SampleClientId") .secret("secret") .authorizedGrantTypes("authorization_code") .scopes("user_info") .autoApprove(true) ; } @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.authenticationManager(authenticationManager); } }
这里我们只开启authorization_code授权模式,另外这里注意到autoApprove=true,这意味着用户不会被重定向到授权的页面,也不需要手动给请求授权。
3.3 安全配置
这里需要定义一个简单的认证机制
@Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private AuthenticationManager authenticationManager; @Override protected void configure(HttpSecurity http) throws Exception { http.requestMatchers() .antMatchers("/login", "/oauth/authorize") .and() .authorizeRequests() .anyRequest().authenticated() .and() .formLogin().permitAll(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.parentAuthenticationManager(authenticationManager) .inMemoryAuthentication() .withUser("john").password("123").roles("USER"); } }
注意,我们用了in-memory认证,这里只是做一个简单的例子,我们直接把账号密码写到内存里了,真正使用的时候这里要换成自定义的userDetailsService.
3.4 用户终端
一个很简单的返回JSON消息的接口
@RestController public class UserController { @GetMapping("/user/me") public Principal user(Principal principal) { return principal; } }
完整的代码下载链接:Over On Github
参考资料:
http://blog.csdn.net/j754379117/article/details/70175198
http://www.baeldung.com/sso-spring-security-oauth2
- 点赞
- 收藏
- 关注作者
评论(0)