-
网关请求拦截-(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;
@Configuration
public class CheckJwtFilter implements GlobalFilter, Ordered {
@Autowired
private StringRedisTemplate redisTemplate;
@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);
}
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)) {
Boolean hasKey = redisTemplate.hasKey(GatewayConstant.OAUTH_PREFIX + jwt);
if(hasKey) {
return chain.filter(exchange);
}
}
}
}
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));
}
@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;
@Configuration
public class GatewayConfig {
@Autowired
private StringRedisTemplate redisTemplate;
@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)) {
JSONObject jsonObject = JSONUtil.parseObj(s);
if (jsonObject.containsKey("access_token")) {
String access_token = jsonObject.getStr("access_token");
Long expires_in = jsonObject.getLong("expires_in");
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;
@Configuration
public class AuthorizationConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private UserDetailsServiceImpl userDetailsService;
@Bean
public TokenStore tokenStore() {
return new JwtTokenStore(jwtAccessTokenConverter());
}
@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;
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("web")
.secret(passwordEncoder.encode("web-secret"))
.scopes("all")
.authorizedGrantTypes("password")
.accessTokenValiditySeconds(7200)
.redirectUris("https://www.baidu.com")
.and()
.withClient("client")
.secret(passwordEncoder.encode("client-secret"))
.scopes("read")
.authorizedGrantTypes("client_credentials")
.accessTokenValiditySeconds(Integer.MAX_VALUE)
.redirectUris("https://www.baidu.com");
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.userDetailsService(userDetailsService)
.authenticationManager(authenticationManager)
.tokenStore(tokenStore())
.accessTokenConverter(jwtAccessTokenConverter());
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;
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsServiceImpl userDetailsService;
@Bean
public AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
@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;
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private SysUserMapper sysUserMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
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:
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;
@SpringBootApplication
@EnableAuthorizationServer
@EnableEurekaClient
@MapperScan(basePackages = "com.xiaoge.mapper")
public class AuthServerApplication {
public static void main(String[] args) {
SpringApplication.run(AuthServerApplication.class, args);
}
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}
-
postman-Authorization(web web-secret grant_type(认证方式)是AuthorizationConfig类中配置的)