目錄
一、黑馬程序員Java進階教程快速入門Spring Security OAuth2.0認證授權詳解
1、oauth服務
WebSecurityConfig
TokenConfig
AuthorizationServer
改寫密碼校驗邏輯實現類
2、oauth2支持的四種方式:
3、oauth2授權
ResouceServerConfig
TokenConfig
4、gateway
SecurityWebFilterChain 放行 后面的授權配置會校驗(授權配置也有訪問控制)
TokenConfig
WebSecurityConfig
設置上下文
二、學成在線
1、GatewayAuthFilter
一、黑馬程序員Java進階教程快速入門Spring Security OAuth2.0認證授權詳解
1、oauth服務
WebSecurityConfig
fuction:管理訪問控制及哪些請求需要認證,以及需要哪些權限
package com.example.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
// @Bean
// public UserDetailsService userDetailsService() {
// //這里配置用戶信息,這里暫時使用這種方式將用戶存儲在內存中
// InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
// manager.createUser(User.withUsername("zhangsan").password("123").authorities("p1").build());
// manager.createUser(User.withUsername("lisi").password("456").authorities("p2").build());
// return manager;
// }@Beanpublic PasswordEncoder passwordEncoder() {
// //密碼為明文方式
// return NoOpPasswordEncoder.getInstance();//spring用于加密的一個算法//授權碼模式必須是加密形式return new BCryptPasswordEncoder();}@Beanpublic AuthenticationManager authenticationManagerBean() throws Exception {return super.authenticationManagerBean();}//安全攔截機制(最重要)@Overrideprotected void configure(HttpSecurity http) throws Exception {http.csrf().disable().authorizeRequests().antMatchers("/r/r1").hasAnyAuthority("p1").antMatchers().authenticated().anyRequest().permitAll().and().formLogin();}
}
TokenConfig
jwt的相關配置就是解析生成jwt
package com.example.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;/*** @author Mr.Lan* @version 1.0* @ClassName TokenConfig$* @description TODO* @date 2024/5/21 16:59**/@Configuration
public class TokenConfig {private String SIGNING_KEY = "mq123";
// @Bean
// public TokenStore tokenStore() {
// return new InMemoryTokenStore();
// }//定義token存儲方式@Beanpublic TokenStore tokenStore() {return new JwtTokenStore(accessTokenConverter());}//定義jwt校驗@Beanpublic JwtAccessTokenConverter accessTokenConverter() {JwtAccessTokenConverter converter = new JwtAccessTokenConverter();converter.setSigningKey(SIGNING_KEY);return converter;}}
AuthorizationServer
授權管理:主要是oauth2的配置?
如:
@Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) {endpoints.authenticationManager(authenticationManager).authorizationCodeServices(authorizationCodeServices).tokenServices(tokenService()).allowedTokenEndpointRequestMethods(HttpMethod.POST); }
.authenticationManager(authenticationManager)在security中引入 和security聯系起來(密碼認證)
.authorizationCodeServices(authorizationCodeServices)支持授權碼認證
.tokenServices(tokenService())令牌配置
@Override public void configure(AuthorizationServerSecurityConfigurer security) {security.tokenKeyAccess("permitAll()").checkTokenAccess("permitAll()").allowFormAuthenticationForClients(); }
這個就是oauth2支持的接口以及允許哪些請求
生成token、校驗token
@Overridepublic void configure(ClientDetailsServiceConfigurer clients)throws Exception {// // clients.withClientDetails(clientDetailsService); //后面實現clientDetailsService 注入bean后取用clients.withClientDetails(clientDetailsService);//客戶端信息用內存方式 // clients.inMemory()// 使用in‐memory存儲 // .withClient("c1")// client_id // .secret(new BCryptPasswordEncoder().encode("secret")) // .resourceIds("res1")//資源id // .authorizedGrantTypes("authorization_code", // "password", "client_credentials", "implicit", "refresh_token")// 該client允許的授權類型authorization_code,password,refresh_token,implicit,client_credentials // .scopes("all")// 允許的授權范圍與服務端匹配 // .autoApprove(false) // //加上驗證回調地址 // .redirectUris("http://www.baidu.com");//注釋掉內存客戶端自己配置客戶端}
這里的配置信息用數據庫存 也可以直接配置 如果客戶端一個
@Bean public AuthorizationCodeServices authorizationCodeServices(DataSource dataSource) { //采用jdbc模式 自動存放在oauth_code表中 封裝類實現好的return new JdbcAuthorizationCodeServices(dataSource);
授權碼用數據庫存
配置完后就可以生成token以及校驗token
改寫密碼校驗邏輯實現類
package com.example.service.impl;import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.example.entity.LoginUser;
import com.example.entity.User;
import com.example.mapper.UserMapper;
import org.checkerframework.checker.units.qual.A;
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.ObjectUtils;import java.sql.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Objects;/*** @author Mr.Lan* @version 1.0* @ClassName UserDetailsServiceImpl$* @description TODO* @date 2024/5/17 15:52**/
@Service
public class UserDetailsServiceImpl implements UserDetailsService {@AutowiredUserMapper userMapper;@Overridepublic UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {//傳參是用戶姓名//數據庫查詢用戶信息以及權限信息LambdaQueryWrapper<User> userLambdaQueryWrapper = new LambdaQueryWrapper<User>();userLambdaQueryWrapper.eq(User::getUsername,s);User user = userMapper.selectOne(userLambdaQueryWrapper);if( ObjectUtils.isEmpty(user)){throw new RuntimeException("用戶不存在");}//查詢權限信息ArrayList<String> permissions = new ArrayList<>(Arrays.asList("getUser", "getUser1"));LoginUser loginUser = new LoginUser(user,permissions);//返回UserDeatil對象//返回接口的實現類相當于返回了接口return loginUser;//返回后后面會校驗密碼}
}
package com.example.service.impl;import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.common.utils.MapUtils;
import com.alibaba.spring.util.ObjectUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.entity.LoginUser;
import com.example.entity.Result;
import com.example.entity.User;
import com.example.service.UserService;
import com.example.mapper.UserMapper;
import com.example.utils.RedisTemplateUtils;
import io.jsonwebtoken.Claims;
import org.checkerframework.checker.units.qual.A;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapProperties;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Service;import javax.xml.crypto.dsig.keyinfo.RetrievalMethod;
import java.security.Principal;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;import static com.example.utils.JwtUtils.generateJwt;/**
* @author Admin
* @description 針對表【user】的數據庫操作Service實現
* @createDate 2024-05-16 21:03:01
*/
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User>implements UserService{@AutowiredAuthenticationManager authenticationManager;@AutowiredUserMapper userMapper;@AutowiredRedisTemplateUtils redisTemplateUtils;//authenticationManager在login的方法(這里是實現類)調用,就繼續傳遞@Overridepublic Result login(User user) {//當參數是接口時可以傳接口的實現類 創建實現類封裝傳遞 Authentication//new UsernamePasswordAuthenticationToken()的兩個參數 Object 后面要用UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(user.getUsername(),user.getPassword());//這里將返回的時認證后的結果Authentication authenticate = authenticationManager.authenticate(usernamePasswordAuthenticationToken);if(Objects.isNull(authenticate)){throw new RuntimeException("認證失敗");}BeanUtils.copyProperties(authenticate.getPrincipal(),user);//獲取返回中的信息 加密返回tokenHashMap<String, Object> claims = new HashMap<>();LoginUser principal = (LoginUser)authenticate.getPrincipal();claims.put("userId",principal.getUser().getId());String token = generateJwt(claims);//將token存入redis 并以userId為keyredisTemplateUtils.set("login:"+principal.getUser().getId(),principal,432000L);HashMap<String, String> map = new HashMap<>();map.put("token",token);
// String jsonString = JSON.toJSONString(map);return Result.success(400,"登陸成功",map);}@Overridepublic Result loginOut() {Authentication authentication = SecurityContextHolder.getContext().getAuthentication();LoginUser principal = (LoginUser)authentication.getPrincipal();String userId = principal.getUser().getId();//刪除redis中的tokentry {redisTemplateUtils.del("login:"+userId);} catch (Exception e) {e.printStackTrace();return Result.error("退出登錄失敗");}return Result.success("成功退出登錄");}
}
2、oauth2支持的四種方式:
###授權碼模式
###申請授權碼
GET {{auth1}}/auth/oauth/authorize?client_id=c1&response_type=code&scope=all&redirect_uri=http://www.baidu.com###申請令牌
POST {{auth1}}/auth/oauth/token?client_id=c1&client_secret=secret&grant_type=authorization_code&code=yHKHC1&redirect_uri=http://www.baidu.com
Content-Type: application/json{}###簡化模式
GET http://localhost:8041/auth/oauth/authorize?client_id=c1&response_type=token&scope=all&redirect_uri=http://www.baidu.com
Accept: application/json###密碼模式
POST {{auth1}}/auth1/oauth/token?client_id=c1&client_secret=secret&grant_type=password&username=lanjie&password=lanjie
Accept: application/json###客戶端模式
POST {{auth1}}/auth/oauth/token?client_id=c1&client_secret=secret&grant_type=client_credentials
Accept: application/json
授權碼、密碼、簡化、客戶端
3、oauth2授權
ResouceServerConfig
資源服務授權(先校驗token后授權) 主要是授權?
package cn.itcast.order.config;import cn.itcast.order.filter.TokenAuthenticationFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
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.authentication.OAuth2AuthenticationProcessingFilter;
import org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter;
import org.springframework.security.oauth2.provider.token.RemoteTokenServices;
import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;/*** @author Mr.Lan* @version 1.0* @ClassName ResouceServerConfig$* @description TODO* @date 2024/5/22 13:28**/
@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true)
public class ResouceServerConfigJwt extends ResourceServerConfigurerAdapter {//資源服務標識public static final String RESOURCE_ID = "res1";@AutowiredTokenStore tokenStore;@AutowiredTokenAuthenticationFilter tokenAuthenticationFilter;//服務資源配置@Overridepublic void configure(ResourceServerSecurityConfigurer resources) {resources.resourceId(RESOURCE_ID)//資源 id
// .tokenServices(tokenService()).tokenStore(tokenStore).stateless(true);}
//服務端訪問控制@Overridepublic void configure(HttpSecurity http) throws Exception {http.csrf().disable().authorizeRequests().antMatchers("/*").access("#oauth2.hasAnyScope('all')").antMatchers("/**").authenticated()//所有/r/**的請求必須認證通過.anyRequest().permitAll();
// http.addFilterBefore(tokenAuthenticationFilter, OAuth2AuthenticationProcessingFilter.class);}//服務端解析令牌通過遠程調用
// @Bean
// public ResourceServerTokenServices tokenService() {
使用遠程服務請求授權服務器校驗token,必須指定校驗token 的url、client_id,client_secret
// RemoteTokenServices service=new RemoteTokenServices();
// service.setCheckTokenEndpointUrl("http://localhost:8041/auth/oauth/check_token");
// service.setClientId("c1");
// service.setClientSecret("secret");
// return service;
// }}
TokenConfig
package cn.itcast.order.config;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;/*** @author Administrator* @version 1.0**/
@Configuration
public class TokenConfigJwt {String SIGNING_KEY = "mq123";// @Bean
// public TokenStore tokenStore() {
// //使用內存存儲令牌(普通令牌)
// return new InMemoryTokenStore();
// }@Autowiredprivate JwtAccessTokenConverter accessTokenConverter;@Beanpublic TokenStore tokenStore() {return new JwtTokenStore(accessTokenConverter());}@Beanpublic JwtAccessTokenConverter accessTokenConverter() {JwtAccessTokenConverter converter = new JwtAccessTokenConverter();converter.setSigningKey(SIGNING_KEY);return converter;}}
4、gateway
SecurityWebFilterChain 放行 后面的授權配置會校驗(授權配置也有訪問控制)
package com.example.config;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
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.TokenStore;/*** @author Mr.Lan* @version 1.0* @ClassName ResouceServerConfig$* @description TODO* @date 2024/5/22 16:32**/
@Configuration
public class ResouceServerConfig {public static final String RESOURCE_ID = "res1";/*** 統一認證服務(UAA) 資源攔截*/@Configuration@EnableResourceServerpublic class UAAServerConfig extendsResourceServerConfigurerAdapter {@Autowiredprivate TokenStore tokenStore;@Overridepublic void configure(ResourceServerSecurityConfigurer resources){resources.tokenStore(tokenStore).resourceId(RESOURCE_ID).stateless(true);}@Overridepublic void configure(HttpSecurity http) throws Exception {http.authorizeRequests().antMatchers("/uaa/**").permitAll();}}/*** 訂單服務*/@Configuration@EnableResourceServerpublic class OrderServerConfig extendsResourceServerConfigurerAdapter {@Autowiredprivate TokenStore tokenStore;@Overridepublic void configure(ResourceServerSecurityConfigurer resources) {resources.tokenStore(tokenStore).resourceId(RESOURCE_ID).stateless(true);}@Overridepublic void configure(HttpSecurity http) throws Exception {http.authorizeRequests().antMatchers("/order/**").access("#oauth2.hasScope('ROLE_API')");}}}
TokenConfig
package com.example.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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;/*** @author Mr.Lan* @version 1.0* @ClassName TokenConfig$* @description TODO* @date 2024/5/21 16:59**/@Configuration
public class TokenConfig {private String SIGNING_KEY = "mq123";
// @Bean
// public TokenStore tokenStore() {
// return new InMemoryTokenStore();
// }//定義token存儲方式@Beanpublic TokenStore tokenStore() {return new JwtTokenStore(accessTokenConverter());}//定義jwt校驗@Beanpublic JwtAccessTokenConverter accessTokenConverter() {JwtAccessTokenConverter converter = new JwtAccessTokenConverter();converter.setSigningKey(SIGNING_KEY);return converter;}}
WebSecurityConfig
package com.example.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
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.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;@EnableWebFluxSecurity
@Configuration
public class WebSecurityConfig {//安全攔截配置@Beanpublic SecurityWebFilterChain webFluxSecurityFilterChain(ServerHttpSecurity http) {return http.authorizeExchange().pathMatchers("/**").permitAll().anyExchange().authenticated().and().csrf().disable().build();}
}
ps:改視頻使用的是網關與服務之間進行明文token(記得設置資源的訪問控制為都放行,網關以及認證授權了)
設置上下文
SecurityContextHolder.getContext()
package cn.itcast.order.filter;import cn.itcast.order.pojo.UserDTO;
import cn.itcast.order.utils.EncryptUtil;
import cn.itcast.order.utils.HeaderMapRequestWrapper;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpHeaders;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;/*** @author Mr.Lan* @version 1.0* @ClassName TokenAuthenticationFilter$* @description TODO* @date 2024/5/23 11:47**/
@Component
@Slf4j
@Order(Ordered.HIGHEST_PRECEDENCE)
public class TokenAuthenticationFilter extends OncePerRequestFilter{@Overrideprotected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponsehttpServletResponse, FilterChain filterChain) throws ServletException, IOException {Enumeration<String> headerNames = httpServletRequest.getHeaderNames();String token=null;while (headerNames.hasMoreElements()) {String headerName = headerNames.nextElement();String headerValue = httpServletRequest.getHeader(headerName);if(headerName.equals("json-token")){token=headerValue;}// 處理請求頭信息log.info("{}:{}",headerName,headerValue);}String jwt=httpServletRequest.getHeader("jwt");
//原有的請求頭依然存在HeaderMapRequestWrapper requestWrapper = new HeaderMapRequestWrapper(httpServletRequest);requestWrapper.addHeader("Authorization",jwt);
// String token = httpServletRequest.getHeader("json‐token");if (token != null){//1.解析tokenString json = EncryptUtil.decodeUTF8StringBase64(token);JSONObject userJson = JSON.parseObject(json);UserDTO user = new UserDTO();user.setUsername(userJson.getString("principal"));JSONArray authoritiesArray = userJson.getJSONArray("authorities");String [] authorities = authoritiesArray.toArray( newString[authoritiesArray.size()]);
// 2.新建并填充authenticationUsernamePasswordAuthenticationToken authentication = newUsernamePasswordAuthenticationToken(user, null, AuthorityUtils.createAuthorityList(authorities));authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest));//3.將authentication保存進安全上下文SecurityContextHolder.getContext().setAuthentication(authentication);}filterChain.doFilter(requestWrapper, httpServletResponse);}
}
上下文如果授權配置ResouceServerConfig生效 會自動生成上下文??
而資源服務已經關閉了授權配置 因此上下文需要明文token傳遞 然后解析?
目的是 資源服務可以借此獲取用戶信息
完結!!!!?
網關的token是否會轉發
二、學成在線
區別: 在網關設置了全局過濾器實現白名單以及校驗 沒有使用資源配置校驗
1、GatewayAuthFilter
package com.xuecheng.gateway.config;import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
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.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.Set;/*** @author Mr.M* @version 1.0* @description 網關認證過慮器* @date 2022/9/27 12:10*/
@Component
@Slf4j
public class GatewayAuthFilter implements GlobalFilter, Ordered {//白名單private static List<String> whitelist = null;static {//加載白名單try (InputStream resourceAsStream = GatewayAuthFilter.class.getResourceAsStream("/security-whitelist.properties");) {Properties properties = new Properties();properties.load(resourceAsStream);Set<String> strings = properties.stringPropertyNames();whitelist= new ArrayList<>(strings);} catch (Exception e) {log.error("加載/security-whitelist.properties出錯:{}",e.getMessage());e.printStackTrace();}}@Autowiredprivate TokenStore tokenStore;@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {String requestUrl = exchange.getRequest().getPath().value();AntPathMatcher pathMatcher = new AntPathMatcher();//白名單放行for (String url : whitelist) {if (pathMatcher.match(url, requestUrl)) {return chain.filter(exchange);}}//檢查token是否存在String token = getToken(exchange);if (StringUtils.isBlank(token)) {return buildReturnMono("沒有認證",exchange);}//判斷是否是有效的tokenOAuth2AccessToken oAuth2AccessToken;try {oAuth2AccessToken = tokenStore.readAccessToken(token);boolean expired = oAuth2AccessToken.isExpired();if (expired) {return buildReturnMono("認證令牌已過期",exchange);}return chain.filter(exchange);} catch (InvalidTokenException e) {log.info("認證令牌無效: {}", token);return buildReturnMono("認證令牌無效",exchange);}}/*** 獲取token*/private String getToken(ServerWebExchange exchange) {String tokenStr = exchange.getRequest().getHeaders().getFirst("Authorization");if (StringUtils.isBlank(tokenStr)) {return null;}String token = tokenStr.split(" ")[1];if (StringUtils.isBlank(token)) {return null;}return token;}private Mono<Void> buildReturnMono(String error, ServerWebExchange exchange) {ServerHttpResponse response = exchange.getResponse();String jsonString = JSON.toJSONString(new RestErrorResponse(error));byte[] bits = jsonString.getBytes(StandardCharsets.UTF_8);DataBuffer buffer = response.bufferFactory().wrap(bits);response.setStatusCode(HttpStatus.UNAUTHORIZED);response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");return response.writeWith(Mono.just(buffer));}@Overridepublic int getOrder() {return 0;}
}
未完!
有關springsecurity單獨實現認證授權