本文简要总结一下如果使用Spring Security OAuth和Spring Boot来实现SSO,文末有样例代码。不了解OAuth2.0协议的同学请参考《OAuth2.0协议原理详解》
整个工程包括三个独立的应用,一个认证服务和两个客户端应用,结构非常简单。当一个用户访问客户端应用中被防护的API时,系统会被自动重定向到认证服务,之后我们使用OAuth2.0的Authorization code授权方式来实现认证授权。
我们先从客户端应用入手,使用Spring Boot可以最大程度简化配置
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-security
org.springframework.security.oauth
spring-security-oauth2
org.springframework.boot
spring-boot-starter-thymeleaf
org.thymeleaf.extras
thymeleaf-extras-springsecurity4
客户端的安全配置如下:
@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
前端并非本文的重点,这里简单提一下。客户端有一个非常简单的前端页面
index.html:
Spring Security SSO
Login
securedPage.html:
Secured Page
Welcome, Name
securedPage.html需要用户通过授权才能访问,如果一个未授权的用户访问这个页面,他会被重定向到login页面
org.springframework.boot
spring-boot-starter-web
org.springframework.security.oauth
spring-security-oauth2
本例中我们把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,这意味着用户不会被重定向到授权的页面,也不需要手动给请求授权。
这里需要定义一个简单的认证机制
@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.
一个很简单的返回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
作者:半夜菊花茶
链接:https://www.jianshu.com/p/d94bb118aa43
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。