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传给 ...
随机推荐
- PEP8规范
目录 一 代码编排 二 文档编排 三 空格使用 四 注释 五 文档描述 六 命名规范 七 编码建议 代码编排 1缩进,4个空格,不用tab键(因为可能不同系统tab的空格数不一定) 2每行最大长度79 ...
- Tornado基于MiddleWare做中间件
详细代码如下: 在app.py里添加 # -*- coding:utf-8 -*- from tornado.ioloop import IOLoop from tornado.web import ...
- JSON语法、对象、遍历数组的区别和基本操作
JSON 语法规则 JSON 语法是 javascript 对象表示语法的子集. 数据在名称/值对中 数据由逗号分隔 花括号保存对象 方括号保存数组 JSON 名称/值对 JSON 数据的书写格式是: ...
- Linux基础-兄弟连Linux
Linux基础增强与辅助知识梳理... ------------ Linux学习基础需要学习那些知识 学习Linux对于程序开发的好处 怎样开始学习Linux Linux的学习方法参考 Linux系统 ...
- MySQL权限授权认证详解
MySQL权限授权认证详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.MySQL权限系统介绍1>.权限系统的作用是授予来自某个主机的某个用户可以查询.插入.修改.删除 ...
- Web项目发布的更新
在项目发版后经常需要修改bug,解决出现的各种问题,对项目升级,这时候就需要将之前部署,上线的项目更新版本. 本文就简单说一下一些出现的关键点(不到之处欢迎指教) 1.使用idea,eclipse等工 ...
- org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'cardSalDao' defined in file [E:\GItUp\pointerCard+redis\target\gameCard-1.0-SNAPSHOT\WEB-INF\classes\cn\jbit\dao
错误信息: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'cardSa ...
- Vertica系列: Vertica DB连接负载均衡
背景 谈到负载均衡, 对于数据库集群需要区分几个概念: 运算的负载均衡, Vertica 本身是 MPP 数据库, SQL 操作自动会利用多台机器来加快处理速度. 数据库连接的负载均衡, Vertic ...
- Android几个比较有用的插件
1.Android Drawable Importer 2.Android ButterKnife Zelezny 使用方法,在SetContentView上右键,Generate 3.Androi ...
- extjs.net Combox赋值
1.直接赋值 ].Rows) //遍历获取两个值 { Ext.Net.ListItem listItem = new Ext.Net.ListItem(); //每次创建一个Ext.Net.ListI ...