-
网关请求拦截-(CheckJwtFilter.java)
package com.xiaoge.config; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.xiaoge.constant.GatewayConstant; import org.apache.commons.lang.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.gateway.filter.GatewayFilter; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.context.annotation.Configuration; import org.springframework.core.Ordered; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.io.buffer.DataBufferFactory; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.util.CollectionUtils; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; import java.util.HashMap; import java.util.List; import java.util.Map; /** * @Classname CheckJwtFilter * @Date 2022/3/20 下午4:49 * @Created by xiaoge * @Description TODO */ @Configuration public class CheckJwtFilter implements GlobalFilter, Ordered { @Autowired private StringRedisTemplate redisTemplate; /** * 过滤请求, 判断是否有jwt, 有放行, 没拦截 * @param exchange * @param chain * @return */ @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { ServerHttpRequest request = exchange.getRequest(); // 获取路径 String path = request.getURI().getPath(); // 判断该路径是否在放行路径中 if (GatewayConstant.ALLOW_PATH.contains(path)) { return chain.filter(exchange); } // 判断该请求头中是否包含Authorization HttpHeaders headers = request.getHeaders(); List<String> list = headers.get(GatewayConstant.AUTHORIZATION); if (!CollectionUtils.isEmpty(list)) { String token = list.get(0); if (StringUtils.isNotBlank(token)) { String jwt = token.replace("bearer ", ""); if (StringUtils.isNotBlank(jwt)) { // 看redis是否还有该token Boolean hasKey = redisTemplate.hasKey(GatewayConstant.OAUTH_PREFIX + jwt); if(hasKey) { return chain.filter(exchange); } } } } // 这里就是没有 jwt 了,返回 401 ServerHttpResponse response = exchange.getResponse(); // 设置响应头 response.getHeaders().add("content-type", "application/json;charset=utf-8"); Map<String, Object> map = new HashMap<>(); map.put("code", HttpStatus.UNAUTHORIZED.value()); map.put("msg", "非法访问!"); ObjectMapper objectMapper = new ObjectMapper(); byte[] bytes = null; try { bytes = objectMapper.writeValueAsBytes(map); } catch (JsonProcessingException e) { e.printStackTrace(); } DataBuffer buffer = response.bufferFactory().wrap(bytes); return response.writeWith(Mono.just(buffer)); } /** * 执行顺序 越小越先 要比-1 小 * @return */ @Override public int getOrder() { return -2; } }
-
网关配置把获取到的token存入redis-(GatewayConfig.java)它是把我们访问网关的路由转发到授权微服务
package com.xiaoge.config; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; import com.xiaoge.constant.GatewayConstant; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.gateway.route.RouteLocator; import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.core.StringRedisTemplate; import reactor.core.publisher.Mono; import java.time.Duration; import java.util.concurrent.TimeUnit; /** * @Classname GatewayConfig * @Date 2022/3/20 下午5:18 * @Created by xiaoge * @Description TODO */ @Configuration public class GatewayConfig { @Autowired private StringRedisTemplate redisTemplate; /** * 描述: 给授权专门做的存入 token 路由 * * @param builder: * @return org.springframework.cloud.gateway.route.RouteLocator */ @Bean public RouteLocator routeLocator(RouteLocatorBuilder builder) { return builder .routes() .route("auth-server-router", r -> r.path("/oauth/**") .filters(f -> f.modifyResponseBody(String.class, String.class, (exchanges, s) -> { String path = exchanges.getRequest().getURI().getPath(); if ("/oauth/token".equals(path)) { //如果是登录的请求,那么得到的 s 就是 token 的一套 JSONObject jsonObject = JSONUtil.parseObj(s); // 判断jsonObject 是否包含access_token if (jsonObject.containsKey("access_token")) { //如果包含 access_token 就放进 redis 里面 // token值 String access_token = jsonObject.getStr("access_token"); // 过期时间 Long expires_in = jsonObject.getLong("expires_in"); // 存入redis中 redisTemplate.opsForValue().set(GatewayConstant.OAUTH_PREFIX + access_token, "", Duration.ofSeconds(expires_in)); } } return Mono.just(s); })).uri("lb://auth-server")) .build(); } }
-
授权服务认证配置-(AuthorizationConfig.java)
package com.xiaoge.config; import com.xiaoge.service.impl.UserDetailsServiceImpl; 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.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.configurers.AuthorizationServerEndpointsConfigurer; 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.oauth2.provider.token.store.KeyStoreKeyFactory; import java.security.KeyPair; /** * @Author: ZhangXiao * @DateTime: 2022/4/7 15:59 * @Description: * 1. token的存储 * 2. jwt的转换器 * 3. 第三方应用 * 4. endpoints暴露 */ @Configuration public class AuthorizationConfig extends AuthorizationServerConfigurerAdapter { @Autowired private PasswordEncoder passwordEncoder; @Autowired private AuthenticationManager authenticationManager; @Autowired private UserDetailsServiceImpl userDetailsService; /** * 使用 jwt 存放 token * @return */ @Bean public TokenStore tokenStore() { return new JwtTokenStore(jwtAccessTokenConverter()); } /** * 使用非对称加密的方式 * @return */ @Bean public JwtAccessTokenConverter jwtAccessTokenConverter() { JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter(); // 把私钥读到内存中 ClassPathResource resource = new ClassPathResource("cxs-jwt.jks"); // 创建一个钥匙工厂 KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(resource,"cxs123".toCharArray()); // 获取钥匙, 传入钥匙别名 KeyPair privateKey = keyStoreKeyFactory.getKeyPair("cxs-jwt"); // 设置进转换器里面 jwtAccessTokenConverter.setKeyPair(privateKey); return jwtAccessTokenConverter; } /** * 配置第三方应用 * password 只要是登录都用这个授权方式 * 客户端授权 用于微服务之间自发的进行远程调用时 资源服务器必须要token的情况, 当然也是可以放行服务提供者的接口的 * * * 描述: 一个 web 平台,用于第三方访问 * 一个 sxt 平台,内部访问的,例如 mq 里发起远程调用 * @param clients * @throws Exception */ @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory() // 配置第三方引用 .withClient("web") // 浏览器 .secret(passwordEncoder.encode("web-secret")) // 密码加密方式 .scopes("all") // 作用域 .authorizedGrantTypes("password") // 授权类型 密码授权 .accessTokenValiditySeconds(7200) // token的时间7200秒 .redirectUris("https://www.baidu.com") // token过期从定向地址 .and() // 上面是密码授权方式, 下面是客户端授权 .withClient("client") // 微服务之间自发的调用 .secret(passwordEncoder.encode("client-secret")) // 密码加密方式 .scopes("read") // 只读 业务方面的一个配置 .authorizedGrantTypes("client_credentials") // 授权类型 客户端授权 .accessTokenValiditySeconds(Integer.MAX_VALUE) // token过期时间 66年 .redirectUris("https://www.baidu.com"); // token过期从定向地址 } /** * 暴露出去 * @param endpoints * @throws Exception */ @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints .userDetailsService(userDetailsService) // 暴露密码登录实现类 .authenticationManager(authenticationManager) // 暴露密码登录需要的认证管理器 .tokenStore(tokenStore()) // 暴露tokenStore .accessTokenConverter(jwtAccessTokenConverter()); // 暴露jwt转换器 super.configure(endpoints); } }
-
授权服务安全配置-(WebSecurityConfig.java)
package com.xiaoge.config; import com.xiaoge.service.impl.UserDetailsServiceImpl; 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.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; /** * @Author: ZhangXiao * @DateTime: 2022/4/7 15:56 * @Description: */ @Configuration public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserDetailsServiceImpl userDetailsService; /** * 认证管理器 密码登录需要认证管理器 * @return * @throws Exception */ @Bean public AuthenticationManager authenticationManager() throws Exception { return super.authenticationManager(); } /** * 走自己的登录 密码登录需要的 * @param auth * @throws Exception */ @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { //走自己的登录 auth.userDetailsService(userDetailsService); } }
-
授权服务登录-(UserDetailsServiceImpl.java)
package com.xiaoge.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.xiaoge.constant.AuthConstant; import com.xiaoge.domain.SysUser; import com.xiaoge.mapper.SysUserMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; import java.util.List; /** * @Author: ZhangXiao * @DateTime: 2022/4/7 16:10 * @Description: */ @Service public class UserDetailsServiceImpl implements UserDetailsService { @Autowired private SysUserMapper sysUserMapper; /** * 登录方法 * @param username * @return * @throws UsernameNotFoundException */ @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { // 获取request ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = requestAttributes.getRequest(); // 获取请求头信息 String loginType = request.getHeader(AuthConstant.LOGIN_TYPE); if (StringUtils.isEmpty(loginType)) { return null; } // 选择 switch (loginType) { case AuthConstant.SYS_USER: // 后台用户 就查后台的sys_user表 SysUser sysUser = sysUserMapper.selectOne(new LambdaQueryWrapper<SysUser>().eq( SysUser::getUsername, username )); if (!ObjectUtils.isEmpty(sysUser)) { // 查询权限 List<String> auths = sysUserMapper.findUserAuthsById(sysUser.getUserId()); if (!CollectionUtils.isEmpty(auths)) { // 设置权限 sysUser.setAuths(auths); } } return sysUser; case AuthConstant.MEMBER: // 前台用户 return null; default: return null; } } }
-
授权服务启动类-(AuthServerApplication.class) (一定要加这个认证注解EnableAuthorizationServer, 因为WebSecurityConfig配置了认证管理器)
package com.xiaoge; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; import org.springframework.context.annotation.Bean; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; /** * @Author: ZhangXiao * @DateTime: 2022/4/7 15:36 * @Description: */ @SpringBootApplication @EnableAuthorizationServer // 开启授权服务器 @EnableEurekaClient // 开启eureka客户端 @MapperScan(basePackages = "com.xiaoge.mapper") public class AuthServerApplication { public static void main(String[] args) { SpringApplication.run(AuthServerApplication.class, args); } /** * 密码加密器 * @return */ @Bean public PasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); } }
-
postman-Authorization(web web-secret grant_type(认证方式)是AuthorizationConfig类中配置的)
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)