spring oauth2.0

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://github.com/spring-projects/spring-security-oauth/tree/master/spring-security-oauth2/src/main/java/org/springframework/security/oauth2

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://localhost:18088/oauth/authorize?client_id=client_code&response_type=code&scope=all&redirect_uri=http://ww.baidu.com

http://localhost:9098/oauth/authorize?client_id=client_2&grant_type=password&scope=server&client_id=client_2&client_secret=123456&response_type=code&redirect_uri=http://ww.baidu.com

http://localhost:9098/oauth/authorize?client_id=client_code&client_secret=123456&response_type=code&redirect_uri=http://ww.baidu.com

改版测试

http://localhost:9999/oauth/authorize?client_id=client_code&client_secret=123456&response_type=code&redirect_uri=http://ww.baidu.com

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方式请求:

http://localhost:9098/oauth/token?username=user_1&password=123456&grant_type=password&scope=server&client_id=client_2&client_secret=123456

返回结果

{"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"}




http://localhost:9098/oauth/token?username=user_1&password=123456&grant_type=password&scope=server&client_id=client_2&client_secret=1234567

这时候如果访问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模式

https://blog.csdn.net/qq_44209563/article/details/103755083?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-5.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-5.control

自定义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;
    }
}

http://localhost:9098/oauth/token?username=user_1&password=123456&grant_type=password&scope=server&client_id=client_2&client_secret=123456

{"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 方法

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容