关于java:微服务网关ZuulSpring-securityOauth20Jwt-动态盐值-实现权限控制开放接口平台4

72次阅读

共计 17286 个字符,预计需要花费 44 分钟才能阅读完成。

前言

接后面的内容,咱们用 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: 8082
baba:
security:
jwt:
secret: otherpeopledontknowit
url: /login
header: Authorization
prefix: Bearer
expiration: 86400
language: CN
spring:
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: 123456
ribbon:
ReadTimeout: 5000
SocketTimeout: 5000
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:8761/eureka/
instance:
prefer-ip-address: false
management:
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
    @EnableAuthorizationServer
    public 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
    @EnableResourceServer
    public 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
    @EnableWebSecurity
    public 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 中申请接口

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

正文完
 0