作者 | 代码忘烦恼
责编 | 屠敏
出品 | CSDN 博客
什么是单点登陆 单点登录(英语:Single sign-on,缩写为 SSO),又译为单一签入,一种对于许多相互关连,但是又是各自独立的软件系统,提供访问控制的属性。当拥有这项属性时,当用户登录时,就可以获取所有系统的访问权限,不用对每个单一系统都逐一登录。这项功能通常是以轻型目录访问协议(LDAP)来实现,在服务器上会将用户信息存储到LDAP数据库中。相同的,单一退出(single sign-off)就是指,只需要单一的退出动作,就可以结束对于多个系统的访问权限。 单点登陆带来的好处降低访问第三方网站的风险(不存储用户密码,或在外部管理)
减少因不同的用户名和密码组合而带来的密码疲劳
减少为相同的身份重新输入密码所花费的时间
因减少与密码相关的调用IT服务台的次数而降低IT成本
密码模式(resource owner password credentials)
授权码模式(authorization code)
简化模式(implicit)
客户端模式(client credentials)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.1.6.RELEASEversion>
<relativePath/>
parent>
<groupId>cn.com.scitcgroupId>
<artifactId>spring_sso_parentartifactId>
<version>1.0-SNAPSHOTversion>
<packaging>warpackaging>
<properties>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding>
<java.version>1.8java.version>
<oauth.version>2.3.6.RELEASEoauth.version>
<oauth-auto.version>2.1.6.RELEASEoauth-auto.version>
properties>
project>
开始编写单点登陆
我们在spring_sso_parent 父工程中 添加一个子模块叫oauth_server的SpringBoot工程,
依赖如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<parent>
<groupId>cn.com.scitcgroupId>
<artifactId>spring_sso_parentartifactId>
<version>1.0-SNAPSHOTversion>
parent>
<groupId>cn.com.scitcgroupId>
<artifactId>oauth_serverartifactId>
<version>0.0.1-SNAPSHOTversion>
<name>oauth_servername>
<packaging>warpackaging>
<description>this is oauth2 serverdescription>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-thymeleafartifactId>
dependency>
<dependency>
<groupId>org.springframework.security.oauthgroupId>
<artifactId>spring-security-oauth2artifactId>
<version>${oauth.version}version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
<scope>runtimescope>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
project>
需要注意的是这里的SpringBoot 版本使用的是父模块的版本:
<parent>
<groupId>cn.com.scitcgroupId>
<artifactId>spring_sso_parentartifactId>
<version>1.0-SNAPSHOTversion>
parent>
我们在oauth_server 中创建一个config的包,并且创建一个WebSecurityConfig的类
@Configuration
@Order(1)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.requestMatchers()
.antMatchers("/login")
.antMatchers("/oauth/authorize")
.and()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin().loginPage("/login").permitAll()
.and().csrf().disable();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//使用内存模拟数据库查询的用户
auth.inMemoryAuthentication() //内存认证
.withUser("admin")//admin 内存认证用户名
.password(passwordEncoder().encode("123456"))//被加密的123456密码
.roles("ADMIN");//ROLE_ADMIN的角色
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
这个类使用了两个注解,@Configuration 让这个类成为了一个配置类, @Order(1) 这个注解是优先级,使用优先级来加载。
http.requestMatchers()
.antMatchers("/login")
.antMatchers("/oauth/authorize")
http.requestMatchers() 这个方法下配置的就是security 接收以什么样的请求,我们这里只接受/login和/oauth/authorize的请求 。
.authorizeRequests()
.anyRequest().authenticated()
这两句配置的意思是除了以上请求所有的请求都需要身份认证才能访问。
.formLogin().loginPage("/login").permitAll()
.and().csrf().disable();
这几个配置的意思是采用form表单登陆默认登陆页面是/login,任何人都能访问,关闭csrf的保护。
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//使用内存模拟数据库查询的用户
auth.inMemoryAuthentication()
.withUser("admin")
.password(passwordEncoder().encode("123456"))
.roles("ADMIN");
}
这里采用的是AuthenticationManagerBuilder 允许内存验证,这里我添加了一个用户名为admin 密码是 123456,角色是ADMIN的 一个用户 来模拟数据库查询的用户信息。
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
PasswordEncoder 是Spring 官方提供的一个md5 密码加密器,一般用于密码的加密。
这个就是WebSecurityConfig的配置
下面我们在config中继续创建一个叫OauthServerConfig的类
@Configuration
@EnableAuthorizationServer
public class OauthServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public void configure(final AuthorizationServerSecurityConfigurer security) throws Exception {
security.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()");
}
@Override
public void configure(final ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("handleCilentId")//客户端id
.secret(passwordEncoder.encode("secret"))//客户端密钥
.authorizedGrantTypes("authorization_code")//授权码模式
.scopes("user_info") //授权范围
.autoApprove(true)//开启自动授权
.redirectUris("http://localhost:8882/login") //认证成功重定向
.accessTokenValiditySeconds(10);//设置超时时间
}
}
这个类上也使用了两个注解,@Configuration 这个注解成为Spring的一个配置类,@EnableAuthorizationServer 注解是开启授权服务器认证。
这个类继承了AuthorizationServerConfigurerAdapter 这个类提供了授权服务器策略。
这里我们实现了两个configure 认证策略方法,分别是AuthorizationServerSecurityConfigurer 和 ClientDetailsServiceConfigurer,而AuthorizationServerSecurityConfigurer提供了十几个配置的方法,这里我们不会多去深入。
其中 tokenKeyAccess意思是:oauth2授权服务器会提供一个/oauth/token_key的url来供资源服务器获取公钥,这个方法就是配置获取公钥的权限范围,它使用的是SpEL表达式且默认不开启, 这里我们使用的是permitAll(),让本身的oauth的访问不需要授权。
checkTokenAccess意思是:授权服务器提供一个/oauth/check_token的url来供资源服务器解码令牌,该方法就是配置权限范围,同样使用的是SpEL表达式且默认不开启,我们这里设置的是 isAuthenticated(),检查access_token需要进行授权。
当客户端向认证服务器认证的时候,我们需要判断这个客户端是否通过了认证那么就要使用ClientDetailsServiceConfigurer 它提供了三种认证方式
clients.withClientDetails() :使用数据库认证
clients.jdbc(): 传入一个dataSource 这里可以使用自定义的dataSource
clients.inMemory():内存认证 相当于将认证信息 写死
@Controller
public class LoginController {
@GetMapping("/login")
public String loginPage() {
return "login";
}
}
这里返回的是一个login的 html 页面:
html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>logintitle>
head>
<body>
<h1>标准登陆h1>
<form action="/auth/login" method="post">
username: <input type="text" name="username"/> <br/>
password: <input type="password" name="password"/> <br/>
<button type="submit">登陆button>
form>
body>
html>
在创建一个UserInfoController 用于获取认证成功的用户信息
@RestController
public class UserInfoController {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@RequestMapping("/user")
public ResponseEntity getUser(Principal principal) {
logger.info("principal:" + principal);return new ResponseEntity(principal, HttpStatus.OK);
}
}
applicatin.yml 配置
server:
port: 8880
servlet:
context-path: /auth
然后我们创建2个客户端分别是oauth_client1 和 oauth_client2
oauth_client1 的依赖如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<parent>
<groupId>cn.com.scitcgroupId>
<artifactId>spring_sso_parentartifactId>
<version>1.0-SNAPSHOTversion>
parent>
<groupId>cn.com.scitcgroupId>
<artifactId>oauth_clinet1artifactId>
<version>0.0.1-SNAPSHOTversion>
<packaging>warpackaging>
<name>oauth_clinet1name>
<description>this is client1description>
<properties>
<java.version>1.8java.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-thymeleafartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-securityartifactId>
dependency>
<dependency>
<groupId>org.springframework.security.oauth.bootgroupId>
<artifactId>spring-security-oauth2-autoconfigureartifactId>
<version>${oauth-auto.version}version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
<scope>runtimescope>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
project>
同样创建一个config 包 并且创建一个 Oauth2ClientSeurityConfig这个类
@Configuration
@EnableOAuth2Sso
public class Oauth2ClientSeurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable() //关闭csrf保护
.antMatcher("/**") //使用以任意开头的url
.authorizeRequests() // 配置路径拦截,表明路径访问所对应的权限,角色,认证信息
.antMatchers("/", "/login**") //控制不同的url接受不同权限的用户访问
.permitAll()// 允许所有人访问
.anyRequest()
.authenticated(); //除了以上请求都需要身份认证
}
}
这个类继承了 WebSecurityConfigurerAdapter 这个SpringSecurity的适配器,实现了HttpSecurity 的 configure 方法。这个类也是两个注解 @Configuration 成为一个配置类,
@EnableOAuth2Sso 启用Oauth2的单点登陆。
我们再创建一个controller 包 ,并且创建一个 InfoController
@Controller
public class InfoController {
@GetMapping("/getUser")
public ResponseEntity userPage(Principal principal) {//客户端认证成功后返回这个用户信息return new ResponseEntity(principal, HttpStatus.OK);
}@GetMapping("/")public String indexPage() {return "index";
}
}
index.html 页面
html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>indextitle>
head>
<body>
<h1>请登录授权h1>
<a href="/getUser">logina>
body>
html>
application.yml
auth-server: http://localhost:8880/auth
server:
port: 8881
servlet:
context-path: /
security:
basic:
enabled: false
oauth2:
client:
clientId: handleCilentId
clientSecret: secret
accessTokenUri: ${auth-server}/oauth/token
userAuthorizationUri: ${auth-server}/oauth/authorize
resource:
userInfoUri: ${auth-server}/user
spring:
thymeleaf:
cache: false
auth-server:是目标认证服务器
clientId:目标认证服务器设置的客户端id
clientSecret:目标认证服务器设置的密码
accessTokenUri:从目标认证服务器获取令牌token
userAuthorizationUri:从目标认证服务器请求授权默认url是/oauth/authorize
userInfoUri: 从目标认证服务器上将认证信息Principal通过形参绑定的方法通过URL的方式获取用户信息。
热 文 推 荐
☞《乐队的夏天》很酷?程序员式的摇滚才燃爆了! ☞ 华为操作系统 28 年史 ☞ 快手百度 4.34 亿美元投资知乎;腾讯回应“push团队全部被开”;Android Q Beta 6 发布 | 极客头条 ☞ Facebook 研发可穿戴脑机接口,读心术成真? ☞屌!小哥用 12 个月的时间开发了12款比特币Dapp, 0.00000001 BTC就能玩区块链版"蚂蚁庄园" ☞再见!微服务 ☞没看完这11 条,别说你精通Python装饰器 ☞第四范式戴文渊:AI落地,为什么不能照搬教科书? ☞边看边用!这本 Python 3.6 的书火爆了 IT 圈! 你点的每个“在看”,我都认真当成了喜欢