SpringSecurityOAuth使用JWT Token实现SSO单点登录

⒈认证服务器
1.添加pom依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency> <dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
<version>2.1.2.RELEASE</version>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
2.配置文件相关配置
server.port=10086
server.servlet.context-path=/server
3.Security配置
package cn.coreqi.ssoserver.config; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder; @EnableWebSecurity
public class SsoWebSecurityConfig extends WebSecurityConfigurerAdapter { @Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
} @Qualifier("ssoUserDetailsService")
@Autowired
private UserDetailsService userDetailsService; @Bean
public PasswordEncoder passwordEncoder()
{
//return NoOpPasswordEncoder.getInstance();
//return new BCryptPasswordEncoder();
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
} @Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()
.and()
.authorizeRequests()
.antMatchers("/oauth/*","/login/*").permitAll()
.anyRequest().authenticated() //任何请求都需要身份认证
.and().csrf().disable(); //禁用CSRF
} @Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// auth.inMemoryAuthentication()
// .withUser("fanqi").password("admin").roles("admin");
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
}
⒋用户登录逻辑
package cn.coreqi.ssoserver.service; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component; @Component
public class SsoUserDetailsService implements UserDetailsService { @Autowired
private PasswordEncoder passwordEncoder; @Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return new User(username,passwordEncoder.encode("admin"),
AuthorityUtils.commaSeparatedStringToAuthorityList("ADMIN"));
}
}
5.认证服务器配置
package cn.coreqi.ssoserver.config; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore; @Configuration
@EnableAuthorizationServer //声明当前应用为认证服务器
public class SsoAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { @Autowired
private AuthenticationManager authenticationManagerBean; @Autowired
private PasswordEncoder passwordEncoder; /**
* Token生成过程处理
* @return
*/
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter(){
JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter();
accessTokenConverter.setSigningKey("fanqi"); //Token签名用的密钥
//发出去的令牌需要密钥签名,验令牌的时候也需要令牌来验签,如果他人获知了我们的密钥
//就可以用我们的密钥来签发我们的JWT令牌,JWT唯一的安全性就是密钥
//别人用我们的密钥来签发我们的JWT令牌就可以随意进入我们的系统
return accessTokenConverter;
} @Bean
public TokenStore jwtTokenStore(){
return new JwtTokenStore(jwtAccessTokenConverter());
} /**
* 针对端点的配置
* @param endpoints
* @throws Exception
*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.tokenStore(jwtTokenStore())
.accessTokenConverter(jwtAccessTokenConverter())
.authenticationManager(authenticationManagerBean);
} /**
* 配置当前认证服务器可以给那些应用发令牌
* @param clients
* @throws Exception
*/
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory()
.withClient("coreqi1")
.secret(passwordEncoder.encode("coreqisecret1"))
.authorizedGrantTypes("authorization_code","refresh_token")
.redirectUris("http://localhost:10010/client1/login","http://127.0.0.1:10010/client1/login")
.scopes("all")
.and()
.withClient("coreqi2")
.secret(passwordEncoder.encode("coreqisecret2"))
.authorizedGrantTypes("authorization_code","refresh_token")
.redirectUris("http://localhost:10000/client2/login","http://127.0.0.1:10000/client2/login")
.scopes("all");
} /**
* 针对安全性有关的配置
* @param security
* @throws Exception
*/
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security
.tokenKeyAccess("isAuthenticated()"); //访问认证服务器的tokenKey(Token签名密钥)时需要身份认证
}
}
6.覆写登录授权页面,直接跳过
package cn.coreqi.ssoserver.controller; import org.springframework.security.oauth2.provider.AuthorizationRequest;
import org.springframework.security.web.csrf.CsrfToken;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
import org.springframework.web.util.HtmlUtils; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Iterator;
import java.util.Map; /**
* 登录完不授权直接进入网站
* 授权的页面根据OAuth协议是无法直接跳过去的
* 因此,我们模仿WhitelabelApprovalEndpoint类,在表单逻辑处直接提交
*/
@RestController
@SessionAttributes({"authorizationRequest"})
public class SsoApprovalEndpoint {
@RequestMapping({"/oauth/confirm_access"})
public ModelAndView getAccessConfirmation(Map<String, Object> model, HttpServletRequest request) throws Exception {
final String approvalContent = this.createTemplate(model, request);
if (request.getAttribute("_csrf") != null) {
model.put("_csrf", request.getAttribute("_csrf"));
} View approvalView = new View() {
public String getContentType() {
return "text/html";
} public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
response.setContentType(this.getContentType());
response.getWriter().append(approvalContent);
}
};
return new ModelAndView(approvalView, model);
} protected String createTemplate(Map<String, Object> model, HttpServletRequest request) {
AuthorizationRequest authorizationRequest = (AuthorizationRequest)model.get("authorizationRequest");
String clientId = authorizationRequest.getClientId();
StringBuilder builder = new StringBuilder();
builder.append("<html><body><div style='display:none'><h1>OAuth Approval</h1>");
builder.append("<p>Do you authorize \"").append(HtmlUtils.htmlEscape(clientId));
builder.append("\" to access your protected resources?</p>");
builder.append("<form id=\"confirmationForm\" name=\"confirmationForm\" action=\"");
String requestPath = ServletUriComponentsBuilder.fromContextPath(request).build().getPath();
if (requestPath == null) {
requestPath = "";
} builder.append(requestPath).append("/oauth/authorize\" method=\"post\">");
builder.append("<input name=\"user_oauth_approval\" value=\"true\" type=\"hidden\"/>");
String csrfTemplate = null;
CsrfToken csrfToken = (CsrfToken)((CsrfToken)(model.containsKey("_csrf") ? model.get("_csrf") : request.getAttribute("_csrf")));
if (csrfToken != null) {
csrfTemplate = "<input type=\"hidden\" name=\"" + HtmlUtils.htmlEscape(csrfToken.getParameterName()) + "\" value=\"" + HtmlUtils.htmlEscape(csrfToken.getToken()) + "\" />";
} if (csrfTemplate != null) {
builder.append(csrfTemplate);
} String authorizeInputTemplate = "<label><input name=\"authorize\" value=\"Authorize\" type=\"submit\"/></label></form>";
if (!model.containsKey("scopes") && request.getAttribute("scopes") == null) {
builder.append(authorizeInputTemplate);
builder.append("<form id=\"denialForm\" name=\"denialForm\" action=\"");
builder.append(requestPath).append("/oauth/authorize\" method=\"post\">");
builder.append("<input name=\"user_oauth_approval\" value=\"false\" type=\"hidden\"/>");
if (csrfTemplate != null) {
builder.append(csrfTemplate);
} builder.append("<label><input name=\"deny\" value=\"Deny\" type=\"submit\"/></label></form>");
} else {
builder.append(this.createScopes(model, request));
builder.append(authorizeInputTemplate);
} builder.append("</div><script>document.getElementById('confirmationForm').submit()</script></body></html>");
return builder.toString();
} private CharSequence createScopes(Map<String, Object> model, HttpServletRequest request) {
StringBuilder builder = new StringBuilder("<ul>");
Map<String, String> scopes = (Map)((Map)(model.containsKey("scopes") ? model.get("scopes") : request.getAttribute("scopes")));
Iterator var5 = scopes.keySet().iterator(); while(var5.hasNext()) {
String scope = (String)var5.next();
String approved = "true".equals(scopes.get(scope)) ? " checked" : "";
String denied = !"true".equals(scopes.get(scope)) ? " checked" : "";
scope = HtmlUtils.htmlEscape(scope);
builder.append("<li><div class=\"form-group\">");
builder.append(scope).append(": <input type=\"radio\" name=\"");
builder.append(scope).append("\" value=\"true\"").append(approved).append(">Approve</input> ");
builder.append("<input type=\"radio\" name=\"").append(scope).append("\" value=\"false\"");
builder.append(denied).append(">Deny</input></div></li>");
} builder.append("</ul>");
return builder.toString();
} }
⒉应用A
1.pom依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency> <dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
<version>2.1.2.RELEASE</version>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
2.配置文件相关配置
server.port=10010
server.servlet.context-path=/client1
security.oauth2.client.client-id=coreqi1
security.oauth2.client.client-secret=coreqisecret1
security.oauth2.client.scope=all
security.oauth2.client.user-authorization-uri=http://127.0.0.1:10086/server/oauth/authorize
security.oauth2.client.access-token-uri=http://127.0.0.1:10086/server/oauth/token
security.oauth2.resource.jwt.key-uri=http://127.0.0.1:10086/server/oauth/token_key
3.主程序类添加@EnableOAuth2Sso注解使之生效
package cn.coreqi; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso; @SpringBootApplication
@EnableOAuth2Sso //使sso生效
public class SsoClient1Application { public static void main(String[] args) {
SpringApplication.run(SsoClient1Application.class, args);
} }
4.编写Action接口用于查看授权信息
package cn.coreqi.sso_client1.controller; import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; @RestController
@RequestMapping("/user")
public class UserController { @GetMapping
public Authentication user(Authentication user){
return user;
}
}
5.编写resources/static/index.html文件,用于跳转测试
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>SSO Client1</title>
</head>
<body>
<h1> SSO Demo Client1</h1>
<a href="http://127.0.0.1:10000/client2/index.html">访问Client2</a>
</body>
</html>
⒊应用B
1.添加pom依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency> <dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
<version>2.1.2.RELEASE</version>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
2.配置文件相关配置
server.port=10000
server.servlet.context-path=/client2
security.oauth2.client.client-id=coreqi2
security.oauth2.client.client-secret=coreqisecret2
security.oauth2.client.scope=all
security.oauth2.client.user-authorization-uri=http://127.0.0.1:10086/server/oauth/authorize
security.oauth2.client.access-token-uri=http://127.0.0.1:10086/server/oauth/token
security.oauth2.resource.jwt.key-uri=http://127.0.0.1:10086/server/oauth/token_key
3.主程序类添加@EnableOAuth2Sso注解使之生效
package cn.coreqi; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso; @SpringBootApplication
@EnableOAuth2Sso //使sso生效
public class SsoClient2Application { public static void main(String[] args) {
SpringApplication.run(SsoClient2Application.class, args);
} }
4.编写Action接口用于查看授权信息
package cn.coreqi.sso_client2.controller; import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; @RestController
@RequestMapping("/user")
public class UserController { @GetMapping
public Authentication user(Authentication user){
return user;
}
}
5.编写resources/static/index.html文件,用于跳转测试
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>SSO Client2</title>
</head>
<body>
<h1> SSO Demo Client2</h1>
<a href="http://127.0.0.1:10010/client1/index.html">访问Client1</a>
</body>
</html>
SpringSecurityOAuth使用JWT Token实现SSO单点登录的更多相关文章
- 170810、spring+springmvc+Interceptor+jwt+redis实现sso单点登录
在分布式环境中,如何支持PC.APP(ios.android)等多端的会话共享,这也是所有公司都需要的解决方案,用传统的session方式来解决,我想已经out了,我们是否可以找一个通用的方案,比如用 ...
- 使用JWT+RSA完成SSO单点登录
无状态登录原理 1.1.什么是有状态? 有状态服务,即服务端需要记录每次会话的客户端信息,从而识别客户端身份,根据用户身份进行请求的处理,典型的设计如tomcat中的session. 例如登录:用户登 ...
- Spring Cloud云架构 - SSO单点登录之OAuth2.0 根据token获取用户信息(4)
上一篇我根据框架中OAuth2.0的使用总结,画了SSO单点登录之OAuth2.0 登出流程,今天我们看一下根据用户token获取yoghurt信息的流程: /** * 根据token获取用户信息 * ...
- Spring Security OAuth2 SSO 单点登录
基于 Spring Security OAuth2 SSO 单点登录系统 SSO简介 单点登录(英语:Single sign-on,缩写为 SSO),又译为单一签入,一种对于许多相互关连,但是又是各自 ...
- SSO单点登录的发展由来以及实现原理【转】
单点登录以及权限,在很早之前都有写过,不过都比较简单,今天就具体说一下,以及下一步要做的 1.web单系统应用 早期我们开发web应用都是所有的包放在一起打成一个war包放入tomcat容器来运行的, ...
- sso单点登录系统
sso单点登录概念 1.一处登录,处处登录.会单独做一个单点登录系统,只负责颁发token和验证token,和页面登录功能. 2.通过在浏览器cookie中放入token,和在redis中对应toke ...
- 使用 JSONP 实现简单的 SSO 单点登录
SSO 即 Single Sign On(单点登录). 一.二级域名之间的单点登录 不需要用到JSONP 或者 p3p 协议,直接使用 COOKIE 就行了,因为顶级域名相同就能实现 COOKIE ...
- [sso] 单点登录认证流程
一.流程说明 第一步:访问cas过滤链接ssoLogin,拼凑定向到 CAS_SERVER 获取ticket的URL 第二步:CAS_SERVER校验用户信息,生成Ticket 第三步:重新定向到访问 ...
- php sso单点登录原理阐述
原理:就是用户登录了单点登录系统(sso)之后,就可以免登录形式进入相关系统: 实现: 点击登录跳转到SSO登录页面并带上当前应用的callback地址 登录成功后生成COOKIE并将COOKIE传给 ...
随机推荐
- 关于python的315道题
python基础篇 为什么学习Python? 通过什么途径学习的Python? Python和Java.PHP.C.C#.C++等其他语言的对比? 简述解释型和编译型编程语言? Python解释器种类 ...
- Xenserver之设置Xenserver和VM机开机自动启动
一.设置Xenserver开机自动启动 [root@xenserver-DS-TestServer09 ~]# xe pool-list uuid ( RO) : b1c803a6-88cf-7b24 ...
- weblogic创建控制台启动脚本以及创建服务器
一.创建控制台脚本 二.创建认证文件 通过上面创建的脚本进行启动的时候,会因为密码问题导致起不来,因为在startWebLogic.sh文件中,没有配置用户名和密码.而且通过上面创建的脚本,启动的时候 ...
- 【JS】js将 /Date(1518943415760+0800)/ 转换为2018-2-18 16:43:35格式
function formatDate(dt) { var year = dt.getFullYear(); var month = dt.getMonth() + 1; var date = dt. ...
- Spring Boot笔记九:AOP面向切面编程
我参考的这篇文章,以验证身份为例讲解了什么是AOP AOP 这里只讲解一下怎么去实现AOP 新建一个类,叫HttpAspect用来切面 package com.vae.springboot.study ...
- Oracle DataBase 编码格式
sqlplus 查询 Oracle 数据库结果乱码或显示 ? 则需要设置字符集 一.客户端字符集 格式:NLS_LANG=language_territory.charset Language: 指定 ...
- SonarQube(代码质量管理)配置与使用
继 Sonarqube(代码质量管理)环境搭建,交大家如何配置与使用Sonarqube 1: 汉化Sonarqube: 找到 Setting --> Update Center --> A ...
- spring cloud学习填坑笔记
最近在学习spring cloud,由于学习资料具有普遍性,部分应个人原因导致的小细节问题,往往很难找到解决的办法.这特别记录一下自己遇到的一些问题. 一.eureka-server加入securit ...
- IPython--转
IPython使用学习笔记 学习<利用python进行数据分析>第三章 IPython:一种交互式计算和开发环境的笔记,共享给大家,同时为自己作为备忘用. 安装ipython用pip即可. ...
- 搭建idea下的vue工程
需要先安装好nodejs和npm 输入下面的命令查看是否成功安装 node -v npm -v 一.开始 工作目录:IdeaProjects使用idea新建Static Web项目:demo 在dem ...