在线演示
用户名:admin 密码:admin
Spring Security Oauth2
OAuth是一个关于授权的开放网络标准,在全世界得到的广泛的应用,目前是2.0的版本。OAuth2在“客户端”与“服务提供商”之间,设置了一个授权层(authorization layer)。“客户端”不能直接登录“服务提供商”,只能登录授权层,以此将用户与客户端分离。“客户端”登录需要获取OAuth提供的令牌,否则将提示认证失败而导致客户端无法访问服务。关于OAuth2这里就不多作介绍了,网上资料详尽。下面我们实现一个 整合 SpringBoot 、Spring Security OAuth2 来实现单点登录功能的案例并对执行流程进行详细的剖析。
案例实现
项目介绍
这个单点登录系统包括下面几个模块:
spring-oauth-parent : 父模块,管理打包
spring-oauth-server : 认证服务端、资源服务端(端口:8881)
spring-oauth-client : 单点登录客户端示例(端口:8882)
spring-oauth-client2: 单点登录客户端示例(端口:8883)
当通过任意客户端访问资源服务器受保护的接口时,会跳转到认证服务器的统一登录界面,要求登录,登录之后,在登录有效时间内任意客户端都无需再登录。
认证服务端
添加依赖
主要是添加 spring-security-oauth2 依赖。
pom.xml
4.0.0
spring-oauth-server
spring-oauth-server
war
com.louis
spring-oauth-parent
1.0.0-SNAPSHOT
org.springframework.boot
spring-boot-starter-web
org.springframework.security.oauth
spring-security-oauth2
${oauth.version}
org.springframework.boot
spring-boot-starter-freemarker
配置文件
配置文件内容如下。
application.yml
server:
port: 8881servlet:
context-path: /auth
启动类
启动类添加 @EnableResourceServer 注解,表示作为资源服务器。
OAuthServerApplication.java
packagecom.louis.spring.oauth.server;
importorg.springframework.boot.SpringApplication;
importorg.springframework.boot.autoconfigure.SpringBootApplication;
importorg.springframework.boot.web.servlet.support.SpringBootServletInitializer;
importorg.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
@SpringBootApplication
@EnableResourceServer
public class OAuthServerApplication extendsSpringBootServletInitializer {
public static voidmain(String[] args) {
SpringApplication.run(OAuthServerApplication.class, args);
}
}
认证服务配置
添加认证服务器配置,这里采用内存方式获取,其他方式获取在这里定制即可。
OAuthServerConfig.java
packagecom.louis.spring.oauth.server.config;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.context.annotation.Configuration;importorg.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;importorg.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;importorg.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;importorg.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;importorg.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
@Configuration
@EnableAuthorizationServerpublic class OAuthServerConfig extendsAuthorizationServerConfigurerAdapter {
@AutowiredprivateBCryptPasswordEncoder passwordEncoder;
@Overridepublic void configure(final AuthorizationServerSecurityConfigurer oauthServer) throwsException {
oauthServer.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()");
}
@Overridepublic void configure(final ClientDetailsServiceConfigurer clients) throwsException {
clients.inMemory()
.withClient("SampleClientId") //clientId, 可以类比为用户名
.secret(passwordEncoder.encode("secret")) //secret, 可以类比为密码
.authorizedGrantTypes("authorization_code") //授权类型,这里选择授权码
.scopes("user_info") //授权范围
.autoApprove(true) //自动认证
.redirectUris("http://localhost:8882/login","http://localhost:8883/login") //认证成功重定向URL
.accessTokenValiditySeconds(10); //超时时间,10s
}
}
安全配置
Spring Security 安全配置。在安全配置类里我们配置了:
1. 配置请求URL的访问策略。
2. 自定义了同一认证登录页面URL。
3. 配置用户名密码信息从内存中创建并获取。
SecurityConfig.java
packagecom.louis.spring.oauth.server.config;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.core.annotation.Order;importorg.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;importorg.springframework.security.config.annotation.web.builders.HttpSecurity;importorg.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;importorg.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
@Configuration
@Order(1)public class SecurityConfig extendsWebSecurityConfigurerAdapter {
@Overrideprotected void configure(HttpSecurity http) throwsException {
http.requestMatchers()
.antMatchers("/login")
.antMatchers("/oauth/authorize")
.and()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin().loginPage("/login").permitAll() //自定义登录页面,这里配置了 loginPage, 就会通过 LoginController 的 login 接口加载登录页面
.and().csrf().disable();
}
@Overrideprotected void configure(AuthenticationManagerBuilder auth) throwsException {//配置用户名密码,这里采用内存方式,生产环境需要从数据库获取
auth.inMemoryAuthentication()
.withUser("admin")
.password(passwordEncoder().encode("123"))
.roles("USER");
}
@BeanpublicBCryptPasswordEncoder passwordEncoder(){return newBCryptPasswordEncoder();
}
}
接口提供
这里提供了一个自定义的登录接口,用于跳转到自定义的同一认证登录页面。
LoginController.java
packagecom.louis.spring.oauth.server.controller;importorg.springframework.stereotype.Controller;importorg.springframework.web.bind.annotation.GetMapping;
@Controllerpublic classLoginController {/*** 自定义登录页面
*@return
*/@GetMapping("/login")publicString login() {return "login";
}
}
登录页面放置在 resources/templates 下,需要在登录时提交 pos t表单到 auth/login。
login.ftl
Insert title here统一认证登录平台
重 置
登 录
newVue({
el :'#app',
data : {
loading:false,
username:'admin',
password:'123'},
methods : {
}
})
border-radius: 5px;-moz-border-radius: 5px;
background-clip: padding-box;
margin: 100px auto;
width: 320px;
padding: 35px 35px 15px 35px;
background: #fff;
border: 1px solid #eaeaea;
box-shadow: 0 025px #cac6c6;
}
.title {
margin: 0px auto 20px auto;
text-align: center;
color: #505458;
}
这里提供了一个受保护的接口,用于获取用户信息,客户端访问这个接口的时候要求登录认证。
UserController.java
packagecom.louis.spring.oauth.server.controller;importjava.security.Principal;importorg.springframework.web.bind.annotation.RequestMapping;importorg.springframework.web.bind.annotation.RestController;
@RestControllerpublic classUserController {/*** 资源服务器提供的受保护接口
*@paramprincipal
*@return
*/@RequestMapping("/user")publicPrincipal user(Principal principal) {
System.out.println(principal);returnprincipal;
}
}
客户端实现
添加依赖
主要添加 Spring Security 依赖,另外因为 Spring Boot 2.0 之后代码的合并, 需要添加 spring-security-oauth2-autoconfigure ,才能使用 @EnableOAuth2Sso 注解。
pom.xml
4.0.0
spring-oauth-client
spring-oauth-client
war
com.louis
spring-oauth-parent
1.0.0-SNAPSHOT
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-security
org.springframework.security.oauth.boot
spring-security-oauth2-autoconfigure
${oauth-auto.version}
org.springframework.boot
spring-boot-starter-thymeleaf
org.thymeleaf.extras
thymeleaf-extras-springsecurity4
启动类
启动类需要添加 RequestContextListener,用于监听HTTP请求事件。
OAuthClientApplication.java
packagecom.louis.spring.oauth.client;
importorg.springframework.boot.SpringApplication;
importorg.springframework.boot.autoconfigure.SpringBootApplication;
importorg.springframework.boot.web.servlet.support.SpringBootServletInitializer;
importorg.springframework.context.annotation.Bean;
importorg.springframework.web.context.request.RequestContextListener;
@SpringBootApplication
public class OAuthClientApplication extendsSpringBootServletInitializer {
@Bean
publicRequestContextListener requestContextListener() {
return newRequestContextListener();
}
public static voidmain(String[] args) {
SpringApplication.run(OAuthClientApplication.class, args);
}
}
安全配置
添加安全配置类,添加 @EnableOAuth2Sso 注解支持单点登录。
OAuthClientSecurityConfig.java
packagecom.louis.spring.oauth.client.config;importorg.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso;importorg.springframework.context.annotation.Configuration;importorg.springframework.security.config.annotation.web.builders.HttpSecurity;importorg.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@EnableOAuth2Sso
@Configurationpublic class OAuthClientSecurityConfig extendsWebSecurityConfigurerAdapter {
@Overridepublic void configure(HttpSecurity http) throwsException {
http.csrf().disable()
.antMatcher("/**")
.aut