oauth2.0调查
目前项目需要在我们自己的app中嵌入别人第三方的页面,这就涉及到了一个用户信息传递的过程.
目前打算效仿微信或者oauth2.0的流程来实现这个过程
现有的样例微信授权登陆过程.
https://developers.weixin.qq.com/community/develop/doc/0006e861258bd8792419946025b000
这里的sessionkey 其实和oauth2.0里的access Token 是一个意思
window.location="https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx87427b31849c8570&response_type=code&scope=snsapi_userinfo&state=1&redirect_uri=http://kq.dapark.top/home/static/html/oauth.html#wechat_redirect";
curl -X post -H "Authorization:681A2DF4371D1469547825ACF3E14267" http://192.168.213.91:2024/oauthsrv/authentication/authorization/code?client_id=demo&response_type=code&state=1&scope=all
681A2DF4371D1469547825ACF3E14267
http://xx.xx.xx.xx:36379/oauthsrv/oauth/token?grant_type=authorization_code&scope=all&code=MztV02&client_id=demo&client_secret=demo&redirect_uri=http://www.baidu.com
@RequestMapping(value="oauth/token", method = RequestMethod.GET, produces = "application/json")
@ResponseBody
public ResultDTO getOauthAccessTokenByCode(HttpServletRequest request)throws Exception{
String code = request.getParameter("code");
logger.info("code"+code);
//===获取code后,请求以下链接获取access_token 和openid
// https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
//
WeixinOauthAccessTokenResult oauthResult = WeixinUtil.getOauthAccessToken(code);
logger.debug("accessToken:" + oauthResult.getAccess_token());
logger.debug("openid:" + oauthResult.getOpenid());
//=============去查我们的数据库里用没有这个openid 如果有的话直接做seession==================
//去拿userInfo
HashMap<String,String> paras = new HashMap();
paras.put("weichat",oauthResult.getOpenid());
List<SysUser> sysUsers = sysUserService.listByParams(paras);
//==如果没有的话 先缓存这个openid 让用户去完成注册
if(sysUsers==null || sysUsers.size()==0){
//插入一条心的数据 让用户重新输入手机号码
this.setSessionParam(request,WeixinConstants.WEIXIN_SESSION_OPENID,oauthResult.getOpenid());
WeixinUser weixinUser = WeixinUtil.getUserInfo(oauthResult.getAccess_token(),oauthResult.getOpenid());
this.setSessionAttribute(request,WeixinConstants.WEIXIN_SESSION_USER,weixinUser);
return this.getResult(29905568, ErrorMessage.getErrorMsg("err.weixin.openid.no.user"));
}else{
//直接拿到用户直接登录
this.setSessionAttribute(request,Constants.SESSION_USER,sysUsers.get(0));
//并将用户信息同步到用户表中
return this.getResult();
}
}
/**
* @Author: dozen.zhang
* @Description: 通过前台http接口获得的code 到后台去获取accessTOken
* @Date: 2018/1/21
*/
public static WeixinOauthAccessTokenResult getOauthAccessToken(String code)throws Exception{
//从缓存中拿取token
// { "access_token":"ACCESS_TOKEN",
// "expires_in":7200,
// "refresh_token":"REFRESH_TOKEN",
// "openid":"OPENID",
// "scope":"SCOPE" }
String accessToken = CacheUtil.get(WeixinConstants.WEIXIN_OAUTH_ACCESSTOKEN_REDIS_KEY+code);
String openId = CacheUtil.get(WeixinConstants.WEIXIN_OAUTH_APPID_REDIS_KEY+code);
// String.format(url,ConfigUtil.getConfig(WeixinConstants.WEIXIN_APPID),)
if(!StringUtil.isBlank(accessToken) && !StringUtil.isBlank(openId)){//如果有值直接返回
WeixinOauthAccessTokenResult weixinOauthToken = new WeixinOauthAccessTokenResult();
weixinOauthToken.setAccess_token(accessToken);
weixinOauthToken.setOpenid(openId);
return weixinOauthToken;
}
//缓存中没有去请求
WeixinOauthAccessTokenResult weixinOauthAccessTokenResult = requestOauthAccessToken(code);
//==============请求成功把token 放入缓存 并设置超时时间============================
CacheUtil.set(WeixinConstants.WEIXIN_OAUTH_ACCESSTOKEN_REDIS_KEY+code,weixinOauthAccessTokenResult.getAccess_token(),weixinOauthAccessTokenResult.getExpires_in());//放入缓存
CacheUtil.set(WeixinConstants.WEIXIN_OAUTH_APPID_REDIS_KEY+code,weixinOauthAccessTokenResult.getOpenid(),weixinOauthAccessTokenResult.getExpires_in());//放入缓存
return weixinOauthAccessTokenResult;
}
oauth2.0 概念
参考网页资料
https://www.cnblogs.com/cjsblog/p/9184173.html
https://blog.csdn.net/u013435893/article/details/79735097
阮一峰 http://www.ruanyifeng.com/blog/2019/04/oauth-grant-types.html
阮一峰 http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html
4种模式
客户端必须得到用户的授权(authorization grant),才能获得令牌(access token)。OAuth 2.0定义了四种授权方式。
-
授权码模式(authorization code). authorization_code refresh_token
需要经过用户同意从auth server 获取code 再用这个code 经过第三方服务器获取 access_token 再用这个token 去资源服务器获取所需的信息
(A)用户访问客户端,后者将前者导向认证服务器。
(B)用户选择是否给予客户端授权。
(C)假设用户给予授权,认证服务器将用户导向客户端事先指定的"重定向URI"(redirection URI),同时附上一个授权码。
(D)客户端收到授权码,附上早先的"重定向URI",向认证服务器申请令牌。这一步是在客户端的后台的服务器上完成的,对用户不可见。
(E)认证服务器核对了授权码和重定向URI,确认无误后,向客户端发送访问令牌(access token)和更新令牌(refresh token)。
-
简化模式(implicit) simple
(A)客户端将用户导向认证服务器。
(B)用户决定是否给于客户端授权。
(C)假设用户给予授权,认证服务器将用户导向客户端指定的"重定向URI",并在URI的Hash部分包含了访问令牌。
(D)浏览器向资源服务器发出请求,其中不包括上一步收到的Hash值。
(E)资源服务器返回一个网页,其中包含的代码可以获取Hash值中的令牌。
(F)浏览器执行上一步获得的脚本,提取出令牌。
(G)浏览器将令牌发给客户端。
密码模式(resource owner password credentials) password
客户端模式(client credentials) client_credentials
spring 项目搭建
参考 https://www.jianshu.com/p/3427549a148a
这里创建了3个服务
1 eureka-server 注册中心
2 service-auth 用于颁发token
主要依赖
spring-boot-starter-data-redis 把token存到redis中
spring-cloud-starter-netflix-eureka-client 做为EurekaClient
spring-cloud-starter-oauth2 是对spring-cloud-starter-security、spring-security-oauth2、spring-security-jwt这3个依赖的整合
spring-boot-starter-actuator 监控接口
完整的pom
<?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.0</modelVersion>
<groupId>com.uaa.service</groupId>
<artifactId>uaa-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>uaa-service</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Finchley.SR1</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
application.yml
spring:
application:
name: service-auth
redis:
host: 172.16.10.43
database: 0
server:
port: 9098
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
application.yml中配置redis、注册中心
java代码
AuthorizationServerConfiguration
继承 AuthorizationServerConfigurerAdapter
AuthorizationServerConfigurerAdapter 类中3个不同的configure方法分别
-
configure(ClientDetailsServiceConfigurer clients) 用来配置客户端详情服务(ClientDetailsService),客户端详情信息在这里进行初始化,你能够把客户端详情信息写死在这里或者是通过数据库来存储调取详情信息;
//配置token的存储方式
-
configure(AuthorizationServerEndpointsConfigurer endpoints) 用来配置授权(authorization)以及令牌(token)的访问端点和令牌服务(token services),还有token的存储方式(tokenStore);
我是用了redis存储 具体见MyRedisTokenStore 需要继承TokenStore
-
configure(AuthorizationServerSecurityConfigurer security) 用来配置令牌端点(Token Endpoint)的安全约束;
@Configuration @EnableAuthorizationServer public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter { @Autowired AuthenticationManager authenticationManager; @Autowired RedisConnectionFactory redisConnectionFactory; @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { String finalSecret = "{bcrypt}" + new BCryptPasswordEncoder().encode("123456"); // 配置两个客户端,一个用于password认证一个用于client认证 clients.inMemory().withClient("client_1") // .resourceIds(Utils.RESOURCEIDS.ORDER) .authorizedGrantTypes("client_credentials", "refresh_token") .scopes("select") .authorities("oauth2") .secret(finalSecret) .and().withClient("client_2") // .resourceIds(Utils.RESOURCEIDS.ORDER) .authorizedGrantTypes("password", "refresh_token") .scopes("server") .authorities("oauth2") .secret(finalSecret); } @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.tokenStore(new MyRedisTokenStore(redisConnectionFactory)) //配置token 的存储方式 .authenticationManager(authenticationManager) .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST); //允许访问的token 协议 } @Override public void configure(AuthorizationServerSecurityConfigurer security) throws Exception { // 允许表单认证 security.allowFormAuthenticationForClients().tokenKeyAccess("permitAll()") .checkTokenAccess("isAuthenticated()"); } }
SecurityConfiguration
继承WebSecurityConfigurerAdapter
- WebSecurityConfigurerAdapter
- configure(HttpSecurity http) httpSecurity中配置所有请求的安全验证
- 注入Bean UserDetailsService
- 注入Bean AuthenticationManager 用来做验证
- 注入Bean PasswordEncoder
@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Bean
@Override
protected UserDetailsService userDetailsService() {
BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
String finalPassword = "{bcrypt}"+bCryptPasswordEncoder.encode("123456");
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(User.withUsername("user_1").password(finalPassword).authorities("USER").build());
manager.createUser(User.withUsername("user_2").password(finalPassword).authorities("USER").build());
return manager;
}
@Bean
PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
AuthenticationManager manager = super.authenticationManagerBean();
return manager;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.requestMatchers().anyRequest()
.and()
.authorizeRequests()
.antMatchers("/oauth/**").permitAll();
}
}
这里在内在中创建了两个用户user_1和user_2,后续会以存mysql数据的方式来完善。
2.6 暴露Remote Token Services 接口
采用RemoteTokenServices这种方式对token进行验证,如果其他资源服务需要验证token,则需要远程调用授权服务暴露的验证token的api接口,验证token的API接口代码如下:
@RestController
@RequestMapping("/users")
public class UserController {
Logger logger = LoggerFactory.getLogger(UserController.class);
@RequestMapping(value = "/current", method = RequestMethod.GET)
public Principal getUser(Principal principal) {
logger.info(">>>>>>>>>>>>>>>>>>>>>>>>");
logger.info(principal.toString());
logger.info(">>>>>>>>>>>>>>>>>>>>>>>>");
return principal;
}
}
2.7 启动类
@SpringBootApplication
@EnableResourceServer
@EnableEurekaClient
public class ServiceAuthApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceAuthApplication.class, args);
}
}
启动类上加上EnableResourceServer注解开启资源服务,因为程序需要对外暴露获取token的API和验证token的API所以该程序也是一个资源服务器。
到此,授权服务已经配置完成,可以访问,认证类型以password方式
3 service-hi 资源服务器
Pom.xml文件
<?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.0</modelVersion>
<groupId>com.service.hi</groupId>
<artifactId>service-hi</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>service-hi</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Finchley.SR1</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
3.2 配置文件 application.yml
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
server:
port: 8765
spring:
application:
name: service-hi
security:
oauth2:
resource:
user-info-uri: http://localhost:9098/users/current
client:
id: client_2
client-secret: 123456
access-token-uri: http://localhost:9098/oauth/token
grant-type: client_credentials,password
scope: server
security.oauth2.resource.user-info-uri用于获取当前token的用户信息,配置security.oauth2.client的相关信息以及clientId、client-secret等信息要和service-auth中的配置一一对应。
3.3 配置Resource Server
@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/order/**").authenticated(); // 配置order访问控制,必须认证后才可以访问
}
}
添加EnableResourceServer注解开启资源服务的功能,加注解EnableGlobalMethodSecurity开户方法级别的保护,ResourceServerConfigurerAdapter是配置类,configure(HttpSecurity http)中只配置了"/order/**"需要验证。
3.4 配置OAuth2 Client
OAuth2 client用来访问被OAuth2保护的资源,service-hi作为OAuth2 Client,配置如下:
@EnableOAuth2Client
@EnableConfigurationProperties
@Configuration
public class OAuth2ClientConfig {
@Bean
@ConfigurationProperties(prefix = "security.oauth2.client")
public ClientCredentialsResourceDetails clientCredentialsResourceDetails() {
return new ClientCredentialsResourceDetails();
}
@Bean
public RequestInterceptor oauth2FeignRequestInterceptor() {
return new OAuth2FeignRequestInterceptor(new DefaultOAuth2ClientContext(), clientCredentialsResourceDetails());
}
@Bean
public OAuth2RestTemplate clientCredentialsRestTemplate() {
return new OAuth2RestTemplate(clientCredentialsResourceDetails());
}
}
注解EnableOAuth2Client开启了OAuth2 Client功能,注入一个OAuth2RestTemplate 类型的Bean用于向service-auth服务请求。
3.5 写一个端点测试Controller TestEndPointController
@RestController
public class TestEndPointController {
Logger logger = LoggerFactory.getLogger(TestEndPointController.class);
@GetMapping("/product/{id}")
public String getProduct(@PathVariable String id) {
return "product id : " + id;
}
@GetMapping("/order/{id}")
public String getOrder(@PathVariable String id) {
return "order id : " + id;
}
@GetMapping("/getPrinciple")
public OAuth2Authentication getPrinciple(OAuth2Authentication oAuth2Authentication, Principal principal, Authentication authentication) {
logger.info(oAuth2Authentication.getUserAuthentication().getAuthorities().toString());
logger.info(oAuth2Authentication.toString());
logger.info("principal.toString() " + principal.toString());
logger.info("principal.getName() " + principal.getName());
logger.info("authentication: " + authentication.getAuthorities().toString());
return oAuth2Authentication;
}
}
3.6 启动类
@SpringBootApplication
@EnableEurekaClient
public class ServiceHiApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceHiApplication.class, args);
}
}
到此资源服务的配置也完成,我们可以测试,依次启动 eureka-server、service-auth、service-hi
访问http://localhost:8761
测试
接着要获取token
授权码模式
.and()
.withClient("client_3")
// .resourceIds(Utils.RESOURCEIDS.ORDER)
.authorizedGrantTypes("authorization_code", "refresh_token")
.scopes("server")
.authorities("oauth2")
.secret(finalSecret);
改版测试
http://127.0.0.1:9098/oauth/authorize?client_id=client_code&redirect_uri=https://www.baidu.com&response_type=code&scope=all
输入这个url后会跳到AuthorizationEndpoint 的方法里
方法执行的时候会发现 没有principle 即没有系统登陆过
http://127.0.0.1:9999/oauth/authorize?client_id=demo&redirect_uri=https://www.baidu.com&response_type=code&state=dXKsOs&scope=all
默认调用方法
org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint.authorize(
密码模式
1获取token
访问oauth/token获取token信息 get方式请求:
返回结果
{"access_token":"3e50190d-6b9e-42c5-a414-eb0c85eb4b03","token_type":"bearer","refresh_token":"2808b214-0219-411a-b621-857e0e569236","expires_in":43199,"scope":"server"}
如果把密码输错了会返回
<oauth>
<error_description>Bad client credentials</error_description>
<error>invalid_client</error>
</oauth>
但是这样肯定不符合我们开发的要求
因为我们肯定都是json参数返回的怎么能一会二是json 一会儿是xml.
所以用post尝试一下
写一个测试类
@Test
public void postAuthCode(){
String url ="http://localhost:9098/oauth/token?username=user_1&password=123456&grant_type=password&scope=server&client_id=client_2&client_secret=1234561";
RequestBody body = RequestBody.create(JSON, "");
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
try {
Response response = client.newCall(request).execute();
logger.info(response.body().string());
}catch(Exception e){
e.printStackTrace();
}
}
{"error":"invalid_client","error_description":"Bad client credentials"}
这时候如果访问service-hi的http://localhost:8765/order/1 不带token信息:
{
"error": "unauthorized",
"error_description": "Full authentication is required to access this resource"
}
告诉你,没有权限访问该资源
正确的访问姿势:
把access_token部分改成上面获取的token
可以是以url参数形式 http://localhost:8765/order/1?access_token=3e50190d-6b9e-42c5-a414-eb0c85eb4b03
也可以是headers 的authorization中 bearer token 形式
这个order接口是在service-hi里的TestEndPointController 工程里的
order id : 1
不需要验证的资源product http://localhost:8765/product/1
product id : 1
getPrinciple打印出验证信息 http://localhost:8765/getPrinciple :
{
"authorities": [
{
"authority": "USER"
}
],
"details": {
"remoteAddress": "0:0:0:0:0:0:0:1",
"sessionId": "8F2D30BA614CA41B6AA03D2A4650EA3E",
"tokenValue": "e9a93dff-fd58-4af3-b458-01fbb6079416",
"tokenType": "Bearer",
"decodedDetails": null
},
"authenticated": true,
"userAuthentication": {
"authorities": [
{
"authority": "USER"
}
],
"details": {
"authorities": [
{
"authority": "USER"
}
],
"details": {
"remoteAddress": "127.0.0.1",
"sessionId": null,
"tokenValue": "e9a93dff-fd58-4af3-b458-01fbb6079416",
"tokenType": "Bearer",
"decodedDetails": null
},
"authenticated": true,
"userAuthentication": {
"authorities": [
{
"authority": "USER"
}
],
"details": {
"grant_type": "password",
"scope": "server",
"client_secret": "123456",
"client_id": "client_2",
"username": "user_1"
},
"authenticated": true,
"principal": {
"password": null,
"username": "user_1",
"authorities": [
{
"authority": "USER"
}
],
"accountNonExpired": true,
"accountNonLocked": true,
"credentialsNonExpired": true,
"enabled": true
},
"credentials": null,
"name": "user_1"
},
"oauth2Request": {
"clientId": "client_2",
"scope": [
"server"
],
"requestParameters": {
"grant_type": "password",
"scope": "server",
"client_id": "client_2",
"username": "user_1"
},
"resourceIds": [],
"authorities": [
{
"authority": "oauth2"
}
],
"approved": true,
"refresh": false,
"redirectUri": null,
"responseTypes": [],
"extensions": {},
"grantType": "password",
"refreshTokenRequest": null
},
"principal": {
"password": null,
"username": "user_1",
"authorities": [
{
"authority": "USER"
}
],
"accountNonExpired": true,
"accountNonLocked": true,
"credentialsNonExpired": true,
"enabled": true
},
"credentials": "",
"clientOnly": false,
"name": "user_1"
},
"authenticated": true,
"principal": "user_1",
"credentials": "N/A",
"name": "user_1"
},
"clientOnly": false,
"principal": "user_1",
"credentials": "",
"oauth2Request": {
"clientId": null,
"scope": [],
"requestParameters": {},
"resourceIds": [],
"authorities": [],
"approved": true,
"refresh": false,
"redirectUri": null,
"responseTypes": [],
"extensions": {},
"grantType": null,
"refreshTokenRequest": null
},
"name": "user_1"
}
改造
改成jdbc模式
自定义oauthtoken获取接口
接口
@RestController
@RequestMapping("/oauth")
public class TokenController {
Logger logger = LoggerFactory.getLogger(TokenController.class);
@Autowired
@Lazy
private MyRedisTokenStore tokenStore;
@Autowired
private TokenEndpoint tokenEndpoint;
/**
* 重写login接口
*
* @param principal
* @param parameters
* @return
* @throws HttpRequestMethodNotSupportedException
*/
@GetMapping("/token")
@PostMapping("/token")
public Map<String, Object> postAccessToken(Principal principal,
@RequestParam Map<String, String> parameters) throws HttpRequestMethodNotSupportedException {
OAuth2AccessToken accessToken;
accessToken = tokenEndpoint.postAccessToken(principal, parameters).getBody();
Map<String, Object> result = new HashMap();
Map<String, Object> resultMap = new HashMap<>();
// token信息
resultMap.put("access_token", accessToken.getValue());
resultMap.put("token_type", accessToken.getTokenType());
resultMap.put("expires_in", accessToken.getExpiresIn());
resultMap.put("scope", StringUtils.join(accessToken.getScope(), ","));
resultMap.putAll(accessToken.getAdditionalInformation());
// 权限信息
Collection<? extends GrantedAuthority> authorities =
tokenStore.readAuthentication(accessToken).getUserAuthentication().getAuthorities();
List<String> list = new ArrayList<>();
for (GrantedAuthority authority : authorities) {
list.add(authority.getAuthority());
}
resultMap.put("authorities", list);
result.put("data",resultMap);
return result;
}
}
{"data":{"access_token":"5415e0e3-05a1-4e79-b368-32868540d0ac","scope":"server","token_type":"bearer","expires_in":31973,"authorities":["USER"]}}
自定义获取code 授权码的方法.
目前我们的用户觉得有中间的跳转页面 用户体验不太好
https://blog.csdn.net/u013310119/article/details/94613332
服务端最主要的一个配置就是使用 @EnableAuthorizationServer
注解,该注解的作用就是引入了一些 OAuth2 相关的端点,包含以下的端点:
-
AuthorizationEndpoint
根据用户认证获得授权码,有下面两个方法:-
/oauth/authorize
- GET -
/oauth/authorize
- POST
-
-
TokenEndpoint
客户端根据授权码获取 token
-
/oauth/token
- GET -
/oauth/token
- POST
-
-
CheckTokenEndpoint
可以用于远程解码令牌
/oauth/check_token
-
WhitelabelApprovalEndpoint
显示授权服务器的确认页。
/oauth/confirm_access
-
WhitelabelErrorEndpoint
显示授权服务器的错误页
/oauth/error
在官方的示例中,通过下面代码直接指定了视图:
registry.addViewController("/oauth/confirm_access").setViewName("authorize");
如果想跳过这个认证确认的过程,设置autoApprove
为true。
自定义过滤方法
由于speing security的是基于用户名密码的 相比之下现实的业务更加的复杂 包括验证码一些的东西,基本自带的无法使用.
这就要求我们能够开发一套满足自己业务要求的认证方式.
https://www.cnblogs.com/niechen/p/9174096.html
appId和 appSecrt 维护的逻辑。(xzc)
目前有两种设计方式一种是前期试验模式 我们可以将clientId clientSecrect放在内存中 inMemory模式.
后期是可以采用jdbc的方式将clientid clientSecret放在数据库里
获取code 的逻辑(xzc)
获取accessToken的逻辑(xzc)
获取用户信息的逻辑(xzc)
增加了customfilter的确生效了
但是好像找不到UserJoinTimeAuthenticationProvider
自定义获取token 方法