前言

接后面的内容,咱们用 spring security 来实现凋谢接口平台。

受权资源服务


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">    <parent>        <artifactId>zuul-auth</artifactId>        <groupId>com.babaznkj.com</groupId>        <version>1.0-SNAPSHOT</version>    </parent>    <modelVersion>4.0.0</modelVersion>    <artifactId>auth2</artifactId>    <properties>        <maven.compiler.source>8</maven.compiler.source>        <maven.compiler.target>8</maven.compiler.target>    </properties>    <dependencies>        <dependency>            <groupId>com.babaznkj.com</groupId>            <artifactId>common</artifactId>        </dependency>        <!-- mysql驱动 -->        <dependency>            <groupId>mysql</groupId>            <artifactId>mysql-connector-java</artifactId>            <version>${mysql.version}</version>        </dependency>        <!-- mybatis启动器 -->        <dependency>            <groupId>org.mybatis.spring.boot</groupId>            <artifactId>mybatis-spring-boot-starter</artifactId>            <version>${mybatis.starter.version}</version>        </dependency>        <!-- alibaba的druid数据库连接池 -->        <dependency>            <groupId>com.alibaba</groupId>            <artifactId>druid-spring-boot-starter</artifactId>            <version>${druid.starter.version}</version>        </dependency>        <!-- 不是starter,手动配置 -->        <dependency>            <groupId>org.springframework.security.oauth</groupId>            <artifactId>spring-security-oauth2</artifactId>            <version>2.3.4.RELEASE</version>        </dependency>    </dependencies></project>

yml

server:  port: 8082baba:  security:    jwt:      secret: otherpeopledontknowit      url: /login      header: Authorization      prefix: Bearer      expiration: 86400      language: CNspring:  application:    name: backend  datasource:    name: test    url: jdbc:mysql://localhost:3306/baba_icloud_test1?characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai    username: root    password: carry0610A    # druid 连接池    type: com.alibaba.druid.pool.DruidDataSource    driver-class-name: com.mysql.jdbc.Driver  main:    allow-bean-definition-overriding: true # 这个示意容许咱们笼罩OAuth2放在容器中的bean对象,肯定要配置  redis:    host: 192.168.3.119    port: 6379    password: 123456ribbon:  ReadTimeout: 5000  SocketTimeout: 5000eureka:  client:    service-url:      defaultZone: http://127.0.0.1:8761/eureka/  instance:    prefer-ip-address: falsemanagement:  endpoints:    security:      enabled: false    web:      exposure:        include: "*"mybatis:  mapper-locations: classpath:mapper/*.xml    # mapper映射文件地位  type-aliases-package: shuaicj.example.security.backend.entity    # 实体类所在的地位  configuration:    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  • AuthorizationServerConfiguration.java

    package com.baba.security.auth2.auth;import com.baba.security.auth2.entity.JdbcTokenStores;import com.baba.security.auth2.service.impl.MemberUserDetailsService;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.bcrypt.BCryptPasswordEncoder;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.approval.ApprovalStore;import org.springframework.security.oauth2.provider.approval.JdbcApprovalStore;import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;import org.springframework.security.oauth2.provider.code.JdbcAuthorizationCodeServices;import org.springframework.security.oauth2.provider.token.TokenStore;import javax.sql.DataSource;/** * OAuth 受权服务器配置 * https://segmentfault.com/a/1190000014371789 * @author wulongbo * @date 2021-11-11 */@Configuration@EnableAuthorizationServerpublic class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter  {  @Autowired  private DataSource dataSource;  @Autowired  private MemberUserDetailsService userDetailsService;  @Autowired  private AuthenticationManager authenticationManager;  @Autowired  private PasswordEncoder passwordEncoder;  //从数据库中查问出客户端信息  @Bean  public JdbcClientDetailsService clientDetailsService() {      JdbcClientDetailsService jdbcClientDetailsService = new JdbcClientDetailsService(dataSource);      jdbcClientDetailsService.setPasswordEncoder(passwordEncoder);      return jdbcClientDetailsService;  }  //token保留策略  @Bean  public TokenStore tokenStore() {      return new JdbcTokenStores(dataSource);  }  //受权信息保留策略  @Bean  public ApprovalStore approvalStore() {      return new JdbcApprovalStore(dataSource);  }  //受权码模式专用对象  @Bean  public AuthorizationCodeServices authorizationCodeServices() {      return new JdbcAuthorizationCodeServices(dataSource);  }  //指定客户端登录信息起源  @Override  public void configure(ClientDetailsServiceConfigurer clients) throws Exception {      //从数据库取数据      clients.withClientDetails(clientDetailsService());//        // 从内存中取数据//        clients.inMemory()//                .withClient("baidu")//                .secret(passwordEncoder.encode("12345"))//                .resourceIds("mayikt_resource")//                .authorizedGrantTypes(//                        "authorization_code",//                        "password",//                        "client_credentials",//                        "implicit",//                        "refresh_token"//                )// 该client容许的受权类型 authorization_code,password,refresh_token,implicit,client_credentials////                .scopes("read", "write")// 容许的受权范畴//                .scopes("all")// 容许的受权范畴//                .autoApprove(false)//                //加上验证回调地址//                .redirectUris("http://www.baidu.com");//        // 读数据库//        clients.inMemory()//                // appid//                .withClient("mayikt_appid1")//                .secret(passwordEncoder.encode("1234567"))//                // 受权码//                .authorizedGrantTypes("authorization_code")//                // 作用域 示意所有的接口都能够拜访 调配咱们的appid 调用接口的权限//                .scopes("all")//                .resourceIds("mayikt_resource")//                // 用户抉择受权之后,跳转到该地址传递code受权码//                .redirectUris("http://www.mayikt.com/callback");  }  //检测token的策略  @Override  public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {      oauthServer.allowFormAuthenticationForClients()    //容许form表单客户端认证,容许客户端应用client_id和client_secret获取token//                .checkTokenAccess("isAuthenticated()")     //通过验证返回token信息              .checkTokenAccess("permitAll()")     //关上check_token              .tokenKeyAccess("permitAll()")         // 获取token申请不进行拦挡              .passwordEncoder(passwordEncoder);  }  //OAuth2的主配置信息  @Override  public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {      endpoints              .approvalStore(approvalStore())              .authenticationManager(authenticationManager)              .authorizationCodeServices(authorizationCodeServices())              .tokenStore(tokenStore())              .userDetailsService(userDetailsService);  }  @Bean  public PasswordEncoder passwordEncoder() {      return new BCryptPasswordEncoder();  }}
  • BaseClientDetailService.java

    package com.baba.security.auth2.auth;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.security.core.authority.AuthorityUtils;import org.springframework.security.oauth2.provider.ClientDetails;import org.springframework.security.oauth2.provider.ClientDetailsService;import org.springframework.security.oauth2.provider.ClientRegistrationException;import org.springframework.security.oauth2.provider.NoSuchClientException;import org.springframework.security.oauth2.provider.client.BaseClientDetails;import java.util.Arrays;import java.util.HashSet;import java.util.Set;import java.util.concurrent.TimeUnit;/** * 自定义客户端认证 * @author wulongbo * @date 2021-11-11 */public class BaseClientDetailService implements ClientDetailsService {  private static final Logger log = LoggerFactory.getLogger(BaseClientDetailService.class);  @Override  public ClientDetails loadClientByClientId(String clientId) throws ClientRegistrationException {      System.out.println(clientId);      BaseClientDetails client = null;      //这里能够改为查询数据库      if("client".equals(clientId)) {          log.info(clientId);          client = new BaseClientDetails();          client.setClientId(clientId);          client.setClientSecret("{noop}123456");          //client.setResourceIds(Arrays.asList("order"));          client.setAuthorizedGrantTypes(Arrays.asList("authorization_code",                  "client_credentials", "refresh_token", "password", "implicit"));          //不同的client能够通过 一个scope 对应 权限集          client.setScope(Arrays.asList("all", "select"));          client.setAuthorities(AuthorityUtils.createAuthorityList("admin_role"));          client.setAccessTokenValiditySeconds((int) TimeUnit.DAYS.toSeconds(1)); //1天          client.setRefreshTokenValiditySeconds((int)TimeUnit.DAYS.toSeconds(1)); //1天          Set<String> uris = new HashSet<>();          uris.add("http://localhost:8080/auth");          client.setRegisteredRedirectUri(uris);      }      if(client == null) {          throw new NoSuchClientException("No client width requested id: " + clientId);      }      return client;  }}
  • ResourceServerConfiguration.java

    package com.baba.security.auth2.auth;import com.baba.security.auth2.entity.JdbcTokenStores;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.Primary;import org.springframework.http.HttpMethod;import org.springframework.security.config.annotation.web.builders.HttpSecurity;import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;import org.springframework.security.oauth2.provider.token.RemoteTokenServices;import org.springframework.security.oauth2.provider.token.TokenStore;import javax.sql.DataSource;/** * OAuth 资源服务器配置 * * @author wulongbo * @date 2021-11-11 */@Configuration@EnableResourceServerpublic class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {  @Autowired  private DataSource dataSource;  @Autowired  private TokenStore tokenStore;  /**   * 指定token的长久化策略   * 其下有   RedisTokenStore保留到redis中,   * JdbcTokenStore保留到数据库中,   * InMemoryTokenStore保留到内存中等实现类,   * 这里咱们抉择保留在数据库中   *   * @return   */  @Bean  public TokenStore jdbcTokenStore() {      return new JdbcTokenStores(dataSource);  }  @Override  public void configure(ResourceServerSecurityConfigurer resources) throws Exception {      resources.resourceId("mayikt_resource")//指定以后资源的id,十分重要!必须写!              .tokenStore(jdbcTokenStore());//                .tokenStore(tokenStore);//指定保留token的形式  }//    @Primary//    @Bean//    public RemoteTokenServices remoteTokenServices() {//        final RemoteTokenServices tokenServices = new RemoteTokenServices();//        tokenServices.setCheckTokenEndpointUrl("http://localhost:8080/oauth/check_token");//        return tokenServices;//    }  @Override  public void configure(HttpSecurity http) throws Exception {      http.authorizeRequests()              //指定不同申请形式拜访资源所须要的权限,个别查问是read,其余是write。              .antMatchers(HttpMethod.GET, "/**").access("#oauth2.hasScope('read')")              .antMatchers(HttpMethod.POST, "/**").access("#oauth2.hasScope('write')")              .antMatchers(HttpMethod.PATCH, "/**").access("#oauth2.hasScope('write')")              .antMatchers(HttpMethod.PUT, "/**").access("#oauth2.hasScope('write')")              .antMatchers(HttpMethod.DELETE, "/**").access("#oauth2.hasScope('write')")              .and()              .headers().addHeaderWriter((request, response) -> {          response.addHeader("Access-Control-Allow-Origin", "*");//容许跨域          if (request.getMethod().equals("OPTIONS")) {//如果是跨域的预检申请,则一成不变向下传播申请头信息              response.setHeader("Access-Control-Allow-Methods", request.getHeader("Access-Control-Request-Method"));              response.setHeader("Access-Control-Allow-Headers", request.getHeader("Access-Control-Request-Headers"));          }      });  }}
  • SecurityConfig.java

    package com.baba.security.auth2.auth;import com.baba.security.auth2.service.impl.MemberUserDetailsService;import com.baba.security.common.utils.MD5Util;import org.springframework.beans.factory.annotation.Autowired;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.crypto.bcrypt.BCryptPasswordEncoder;import org.springframework.security.crypto.password.PasswordEncoder;import org.springframework.stereotype.Component;/** * 配置咱们httpBasic 登陆账号和明码 */@Component@EnableWebSecuritypublic class SecurityConfig extends WebSecurityConfigurerAdapter {  @Autowired  private MemberUserDetailsService memberUserDetailsService;  @Bean  public PasswordEncoder passwordEncoder() {      return new BCryptPasswordEncoder();  }  @Override  protected void configure(AuthenticationManagerBuilder auth) throws Exception {//        auth.//                inMemoryAuthentication()//                .withUser("mayikt")//                .password(passwordEncoder().encode("654321"))//                .authorities("/*");////        auth.//                inMemoryAuthentication()//                .withUser("baidu")//                .password(passwordEncoder().encode("54321"))//                .authorities("/*");//        System.out.println("===============================");//        System.out.println(passwordEncoder().encode("123456"));//        System.out.println(new BCryptPasswordEncoder().encode("12345"));      auth.userDetailsService(memberUserDetailsService).passwordEncoder(new PasswordEncoder() {          /**           * 对明码MD5           * @param rawPassword           * @return           */          @Override          public String encode(CharSequence rawPassword) {              return MD5Util.encode((String) rawPassword);          }          /**           * rawPassword 用户输出的明码           * encodedPassword 数据库DB的明码           * @param rawPassword           * @param encodedPassword           * @return           */          @Override          public boolean matches(CharSequence rawPassword, String encodedPassword) {              String rawPass = MD5Util.encode((String) rawPassword);              boolean result = rawPass.equals(encodedPassword);              return result;          }      });  }  @Override  protected void configure(HttpSecurity http) throws Exception {      http.authorizeRequests()              .anyRequest().authenticated() //所有申请都须要通过认证              .antMatchers("/auth2/**").permitAll()              .and()              .httpBasic() //Basic登录              .and()              .csrf().disable(); //关跨域爱护  }  @Override  @Bean  public AuthenticationManager authenticationManagerBean() throws Exception {      return super.authenticationManagerBean();  }}
  • ProductController.java

    package com.baba.security.auth2.controller;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;/** * @Author wulongbo * @Date 2021/11/11 19:10 * @Version 1.0 */@RestController@RequestMapping("/product")public class ProductController {  @RequestMapping("/findAll")  public String findAll() {      return "受权胜利";  }  @RequestMapping("/findAll1")  public String findAll1() {      return "受权胜利1";  }}
  • dao和service以及mapper.xml参考后面的就好了
  • JdbcTokenStores.java

    package com.baba.security.auth2.entity;import com.alibaba.druid.support.logging.Log;import com.alibaba.druid.support.logging.LogFactory;import org.springframework.dao.EmptyResultDataAccessException;import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;import org.springframework.security.oauth2.common.OAuth2AccessToken;import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;import javax.sql.DataSource;/** * @Author wulongbo * @Date 2021/11/11 19:56 * @Version 1.0 */public class JdbcTokenStores extends JdbcTokenStore {  private static final Log LOG = LogFactory.getLog(JdbcTokenStores.class);  public JdbcTokenStores(DataSource dataSource) {      super(dataSource);  }  @Override  public OAuth2AccessToken readAccessToken(String tokenValue) {      OAuth2AccessToken accessToken = null;      try {          accessToken = new DefaultOAuth2AccessToken(tokenValue);      }      catch (EmptyResultDataAccessException e) {          if (LOG.isInfoEnabled()) {              LOG.info("Failed to find access token for token "+tokenValue);          }      }      catch (IllegalArgumentException e) {          LOG.warn("Failed to deserialize access token for " +tokenValue,e);          removeAccessToken(tokenValue);      }      return accessToken;  }}
  • Application.java

    package com.baba.security.auth2;import org.mybatis.spring.annotation.MapperScan;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;/** * Spring boot app. * * @author wulongbo 2021/10/18 */@SpringBootApplication@MapperScan("com.baba.security.auth2.dao")public class Application {  public static void main(String[] args) {      SpringApplication.run(Application.class, args);  }}
  • 数据库
INSERT INTO `baba_icloud_test1`.`oauth_client_details` (`client_id`, `resource_ids`, `client_secret`, `scope`, `authorized_grant_types`, `web_server_redirect_uri`, `authorities`, `access_token_validity`, `refresh_token_validity`, `additional_information`, `autoapprove`) VALUES ('baidu', 'mayikt_resource,user,device', '$2a$10$32N/ptu3jY0WjFH0qLbcEO2ZcCg4gYCJvMbwmqzf84qNCcDFBLl4q', 'read,write', 'authorization_code', 'http://www.mayikt.com/callback', NULL, NULL, NULL, NULL, NULL);INSERT INTO `baba_icloud_test1`.`oauth_client_details` (`client_id`, `resource_ids`, `client_secret`, `scope`, `authorized_grant_types`, `web_server_redirect_uri`, `authorities`, `access_token_validity`, `refresh_token_validity`, `additional_information`, `autoapprove`) VALUES ('mayikt_appid', 'mayikt_resource', '$2a$10$6/Sab9Au.CdKqyE4x0gr0OJZrzrcDCWZ9GLqMDF6KX.jHad5vlkeO', 'read,write,trust', 'authorization_code', 'http://www.mayikt.com/callback', NULL, NULL, NULL, NULL, NULL);
  • 测试

拜访
获取到code受权码
依据受权码获取access_token
http://localhost:8082/oauth/token?code=StPCWn&grant_type=authorization_code&redirect_uri=http://www.mayikt.com/callback&Scope=all&client_id=mayikt_appid&client_secret=123456

当然也能够通过网关来拜访:
http://localhost:8080/auth2/oauth/token?code=n6d6hP&grant_type=authorization_code&redirect_uri=http://www.mayikt.com/callback&Scope=all&client_id=mayikt_appid&client_secret=123456
查看access_token
http://localhost:8082/oauth/check_token?token=5c469473-8599-44f2-9061-fbe61be37ff5
调用接口http://localhost:8080/auth2/product/findAll主装Bearer拼接token放入header中申请接口

当然也能够不组装间接申请