一、涉及的技术(绿色背景)
二、基本流程
三、构建项目(基础环境搭建请参考微服务架构-简介_Morik的博客-CSDN博客)
1、构建聚合项目
1.1、打开idea新建一个springBoot项目(注意alibabaCloud、springCloud、springBoot的版本匹配)详细版本请参考官网:版本说明 · alibaba/spring-cloud-alibaba Wiki · GitHub
2、构建公共项目
2.1、idea-->new model-->maven
2.2、pom文件中可以放一些公共的包、后期可以放一些工具类在里边
3、构建资源项目(影视资讯业务)
3.2、同样的方法新建一个model、然后在pom中加入两个核心依赖包
3.3、启动类注册为资源服务器(@EnableResourceServer)
3.4、配置yml
3.5、编写业务接口(通过@AuthenticationPrincipal注解获取token的用户信息)
4、构建网关项目(注意:RestTemplate要手动增强,要保证在Getaway启动完成之前通过负载均衡提前把授权服务器上的公钥拿到。如果在Getaway启动完成后批量的订单请求打过来LB还没初始化完成所有请求没公钥无法鉴权全部作废)
4.1、新建model并在pom中新增核心依赖
umf cn.morik 0.0.1-SNAPSHOT 4.0.0 cn.morik.umf route网关(鉴权、流控) org.springframework.boot spring-boot-starter-webfluxorg.springframework.boot spring-boot-starter-actuatororg.springframework.cloud spring-cloud-starter-gatewaycom.alibaba.cloud spring-cloud-starter-alibaba-sentinelcom.alibaba.cloud spring-cloud-alibaba-sentinel-gatewaycom.alibaba.csp sentinel-datasource-nacoscom.alibaba.cloud spring-cloud-starter-alibaba-nacos-discoveryio.jsonwebtoken jjwt-api0.10.5 io.jsonwebtoken jjwt-impl0.10.5 runtime io.jsonwebtoken jjwt-jackson0.10.5 runtime com.github.xiaoymin knife4j-spring-boot-starter2.0.3 org.springframework.boot spring-boot-configuration-processortrue org.projectlombok lombok
4.2、yml新增主配置和跨域配置
4.8、新建过滤器
package cn.morik.umf.route.filter; import cn.morik.umf.route.exception.GateWayException; import cn.morik.umf.route.utils.MDA; import cn.morik.umf.route.utils.MorikRestTemplate; import cn.morik.umf.route.utils.SystemErrorType; import io.jsonwebtoken.Claims; import io.jsonwebtoken.JwsHeader; import io.jsonwebtoken.Jwt; import io.jsonwebtoken.Jwts; import lombok.extern.slf4j.Slf4j; import org.apache.commons.codec.binary.base64; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.core.Ordered; import org.springframework.http.*; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.stereotype.Component; import org.springframework.util.MultiValueMap; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; import java.security.KeyFactory; import java.security.PublicKey; import java.security.spec.X509EncodedKeySpec; import java.util.linkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; @Component @Slf4j public class AuthorizationFilter implements GlobalFilter, Ordered, InitializingBean { @Autowired private MorikRestTemplate restTemplate; private PublicKey publicKey; private static SetshouldSkipUrl = new linkedHashSet<>(); @Override public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { String reqPath = exchange.getRequest().getURI().getPath(); log.info("网关认证开始URL->:{}", reqPath); //1:不需要认证的url if (shouldSkip(reqPath)) { log.info("无需认证的路径"); return chain.filter(exchange); } //获取请求头 String authHeader = exchange.getRequest().getHeaders().getFirst("Authorization"); //请求头为空 if (StringUtils.isEmpty(authHeader)) { log.warn("需要认证的url,请求头为空"); throw new GateWayException(SystemErrorType.UNAUTHORIZED_HEADER_IS_EMPTY); } //交易我们的jwt 若jwt不对或者超时都会抛出异常 Claims claims = validateJwtToken(authHeader); //向headers中放文件,记得build ServerHttpRequest request = exchange.getRequest().mutate().header("username", claims.get("user_name").toString()).build(); //将现在的request 变成 change对象 ServerWebExchange serverWebExchange = exchange.mutate().request(request).build(); //从jwt中解析出权限集合进行判断 hasPremisson(claims, reqPath); return chain.filter(serverWebExchange); } private Claims validateJwtToken(String authHeader) { String token = null; try { token = StringUtils.substringAfter(authHeader, "bearer "); Jwt parseClaimsJwt = Jwts.parser().setSigningKey(publicKey).parseClaimsJws(token); Claims claims = parseClaimsJwt.getBody(); log.info("claims:{}", claims); return claims; } catch (Exception e) { log.error("校验token异常:{},异常信息:{}", token, e.getMessage()); throw new GateWayException(SystemErrorType.INVALID_TOKEN); } } private boolean hasPremisson(Claims claims, String currentUrl) { boolean hasPremisson = false; //登陆用户的权限集合判断 List premessionList = claims.get("authorities", List.class); for (String url : premessionList) { if (currentUrl.contains(url)) { hasPremisson = true; break; } } if (!hasPremisson) { log.warn("权限不足"); throw new GateWayException(SystemErrorType.FORBIDDEN); } return hasPremisson; } private boolean shouldSkip(String reqPath) { for (String skipPath : shouldSkipUrl) { if (reqPath.contains(skipPath)) { return true; } } return false; } @Override public int getOrder() { return 0; } @Override public void afterPropertiesSet() throws Exception { shouldSkipUrl.add("/oauth/token"); shouldSkipUrl.add("/oauth/check_token"); shouldSkipUrl.add("/user/getCurrentUser"); // shouldSkipUrl.add("/film/api/info"); //初始化公钥 this.publicKey = genPublicKeyByTokenKey(); } private String getTokenKey() { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); headers.setBasicAuth(MDA.clientId, MDA.clientSecret); HttpEntity > entity = new HttpEntity<>(null, headers); try { ResponseEntity
5、构建授权中心
5.1、新建model并导入核心pom依赖
umf cn.morik 0.0.1-SNAPSHOT 4.0.0 cn.morik.umf oauth授权中心 org.springframework.boot spring-boot-starterorg.springframework.boot spring-boot-starter-webcom.alibaba.cloud spring-cloud-starter-alibaba-nacos-discoveryorg.springframework.cloud spring-cloud-starter-oauth2org.mybatis.spring.boot mybatis-spring-boot-starter2.0.0 mysql mysql-connector-javaorg.springframework.boot spring-boot-starter-jdbccom.alibaba druid1.1.8 org.springframework.boot spring-boot-starter-thymeleaforg.springframework.boot spring-boot-starter-testtest org.junit.vintage junit-vintage-enginecom.nimbusds nimbus-jose-jwtRELEASE org.projectlombok lombok
5.2、yml配置
server: port: 8888 spring: application: name: oauth-server cloud: nacos: discovery: server-addr: 192.168.1.9:8848 datasource: username: root password: Morik1234567890. driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://192.168.1.9:3306/morik-user?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=UTF-8&useSSL=false type: com.alibaba.druid.pool.DruidDataSource # redis: # host: 192.168.1.9 # port: 6379 # password: root # session: # store-type: redis # timeout: 1800 logging: level: cn: morik: umf: oauth: config: role: mapper: debug ##A.CTable配置 #mybatis: # #自动更新表 # table: # auto: update # #实体类扫描地址 # model: # pack: cn.morik.umf.oauth.config.role.entity # #数据库类型 # database: # type: mysql
5.3、授权配置
package cn.morik.umf.oauth.indb; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.ClassPathResource; import org.springframework.security.authentication.AuthenticationManager; 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.ClientDetailsService; import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService; import org.springframework.security.oauth2.provider.token.TokenEnhancerChain; import org.springframework.security.oauth2.provider.token.TokenStore; import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter; import org.springframework.security.oauth2.provider.token.store.JwtTokenStore; import org.springframework.security.rsa.crypto.KeyStoreKeyFactory; import javax.sql.DataSource; import java.security.KeyPair; import java.util.Arrays; @Configuration @EnableAuthorizationServer public class AuthServerInDbConfig extends AuthorizationServerConfigurerAdapter { @Autowired private AuthenticationManager authenticationManager; @Autowired private DataSource dataSource; @Autowired private UMFUserDetailService userDetailsService; @Bean public TokenStore tokenStore(){ return new JwtTokenStore(jwtAccessTokenConverter()); } @Bean public JwtAccessTokenConverter jwtAccessTokenConverter() { JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); //jwt的密钥(用来保证jwt 字符串的安全性 jwt可以防止篡改 但是不能防窃听 所以jwt不要 放敏感信息) converter.setKeyPair(keyPair()); //converter.setSigningKey("123456"); return converter; } @Bean public KeyPair keyPair() { KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(new ClassPathResource("jwt.jks"), "123456".toCharArray()); return keyStoreKeyFactory.getKeyPair("jwt", "123456".toCharArray()); } @Bean public UMFTokenEnhancer umfTokenEnhancer() { return new UMFTokenEnhancer(); } @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.withClientDetails(clientDetails()); } @Bean public ClientDetailsService clientDetails() { return new JdbcClientDetailsService(dataSource); } @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain(); tokenEnhancerChain.setTokenEnhancers(Arrays.asList(umfTokenEnhancer(),jwtAccessTokenConverter())); endpoints.tokenStore(tokenStore()) //授权服务器颁发的token 怎么存储的 .tokenEnhancer(tokenEnhancerChain) .userDetailsService(userDetailsService) //用户来获取token的时候需要 进行账号密码 .authenticationManager(authenticationManager); } @Override public void configure(AuthorizationServerSecurityConfigurer security) throws Exception { //第三方客户端校验token需要带入 clientId 和clientSecret来校验 security .checkTokenAccess("isAuthenticated()") .tokenKeyAccess("isAuthenticated()");//来获取我们的tokenKey需要带入clientId,clientSecret security.allowFormAuthenticationForClients(); } }
四、效果展示
1、获取token
2、通过Getaway获取影视资讯服务信息
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)