Security认证授权服务流程图
securtiy认证授权服务
引入依赖
<!--授权--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-oauth2</artifactId> </dependency>
认证授权服务
package com.isoftstone.cloud.security.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.common.exceptions.*;
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.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.error.DefaultWebResponseExceptionTranslator;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import javax.sql.DataSource;
/**
* oauth2.0 授权配置
*/
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private AuthorizationCodeServices authorizationCodeServices;
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private AuthorizationServerTokenServices authorizationServerTokenServices;
@Autowired
@Qualifier("myClientDetailsService")
private ClientDetailsService clientDetailsService;
@Autowired
private PasswordEncoder passwordEncoder;
@Bean("myClientDetailsService")
public ClientDetailsService clientDetailsService(DataSource dataSource) {
JdbcClientDetailsService clientDetailsService = new JdbcClientDetailsService(dataSource);
clientDetailsService.setPasswordEncoder(passwordEncoder);
return clientDetailsService;
}
/**
* 客户端详细信息服务
*/
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.withClientDetails(clientDetailsService);
}
/**
* 令牌访问端点
*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints){
endpoints
.authenticationManager(authenticationManager)
.authorizationCodeServices(authorizationCodeServices)
// 配置令牌管理
.tokenServices(authorizationServerTokenServices)
// 允许post请求
.allowedTokenEndpointRequestMethods(HttpMethod.POST)
// 异常信息处理
.exceptionTranslator(new DefaultWebResponseExceptionTranslator() {
public static final String BAD_MSG = "Bad credentials";
@Override
public ResponseEntity<OAuth2Exception> translate(Exception e) throws Exception {
// e.printStackTrace();
OAuth2Exception oAuth2Exception;
if (e.getMessage() != null && BAD_MSG.equals( e.getMessage())) {
oAuth2Exception = new InvalidGrantException("用户名或密码错误", e);
} else if (e instanceof InvalidTokenException){
oAuth2Exception = new InvalidTokenException(e.getMessage(),e);
}else if (e instanceof InvalidGrantException) {
oAuth2Exception = new InvalidGrantException(e.getMessage(), e);
}else if (e instanceof InternalAuthenticationServiceException) {
oAuth2Exception = new InvalidGrantException(e.getMessage(), e);
} else if (e instanceof RedirectMismatchException) {
oAuth2Exception = new InvalidGrantException(e.getMessage(), e);
} else if (e instanceof InvalidScopeException) {
oAuth2Exception = new InvalidGrantException(e.getMessage(), e);
} else {
oAuth2Exception = new UnsupportedResponseTypeException("服务内部错误", e);
}
ResponseEntity<OAuth2Exception> response = super.translate(oAuth2Exception);
ResponseEntity.status(oAuth2Exception.getHttpErrorCode());
response.getBody().addAdditionalInformation("code", oAuth2Exception.getHttpErrorCode() + "");
response.getBody().addAdditionalInformation("msg", oAuth2Exception.getMessage());
return response;
}
});
}
/**
* 令牌访问端点安全策略
*/
@Override
public void configure(AuthorizationServerSecurityConfigurer security){
security
.tokenKeyAccess("permitAll()")
.checkTokenAccess("permitAll()")
.allowFormAuthenticationForClients();
}
}
token管理配置类
package com.isoftstone.cloud.security.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.JdbcAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
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 javax.sql.DataSource;
import java.util.Collections;
@Configuration
public class TokenConfig {
/**
* 密钥串
*/
private static final String SIGNING_KEY = "uaa";
@Bean
public TokenStore tokenStore(){
return new JwtTokenStore(jwtAccessTokenConverter());
}
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter(){
JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
jwtAccessTokenConverter.setSigningKey(SIGNING_KEY);
return jwtAccessTokenConverter;
}
/**
* 配置令牌管理
*/
@Bean
public AuthorizationServerTokenServices authorizationServerTokenServices(ClientDetailsService clientDetailsService,JwtAccessTokenConverter accessTokenConverter){
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
//客户端令牌信息
defaultTokenServices.setClientDetailsService(clientDetailsService);
//支持刷新令牌
defaultTokenServices.setSupportRefreshToken(true);
//令牌存储策略
defaultTokenServices.setTokenStore(tokenStore());
//令牌增强
TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
tokenEnhancerChain.setTokenEnhancers(Collections.singletonList(accessTokenConverter));
defaultTokenServices.setTokenEnhancer(tokenEnhancerChain);
return defaultTokenServices;
}
/**
* 授权码存储方式;基于数据库
*/
@Bean
public AuthorizationCodeServices authorizationCodeServices(DataSource dataSource){
return new JdbcAuthorizationCodeServices(dataSource);
}
}
认证授权服务web安全配置
package com.isoftstone.cloud.security.config;
import com.isoftstone.cloud.security.handler.FailureHandler;
import com.isoftstone.cloud.security.handler.LoginHandler;
import com.isoftstone.cloud.security.handler.NoAccessDeniedHandler;
import com.isoftstone.cloud.security.handler.SuccessHandler;
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.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(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private SuccessHandler successHandler;
@Autowired
private FailureHandler failureHandler;
@Autowired
private LoginHandler loginHandler;
@Autowired
private NoAccessDeniedHandler noAccessDeniedHandler;
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
public static void main(String[] args) {
PasswordEncoder passwordEncoder=new BCryptPasswordEncoder();
}
@Bean
@Override
protected AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.formLogin()
.loginProcessingUrl("/login").permitAll()
//json化就把这个打开,如果要测试oauth2.0授权码模式就注释掉
// .successHandler(successHandler).permitAll()
// .failureHandler(failureHandler).permitAll()
.and()
.logout().logoutSuccessHandler(loginHandler)
.and()
//其他接口全部拦截,返回403
.authorizeRequests()
.antMatchers("/**").authenticated();
//访问无权限
http.exceptionHandling().accessDeniedHandler(noAccessDeniedHandler);
}
}
自定义失败成功未授权处理器
package com.isoftstone.cloud.security.handler;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.MediaType;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
@Component
public class FailureHandler implements AuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
httpServletResponse.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
HashMap hashMap = new HashMap();
hashMap.put("code", 500);
hashMap.put("data", null);
hashMap.put("msg", e.getLocalizedMessage());
httpServletResponse.getWriter().write(new ObjectMapper().writeValueAsString(hashMap));
}
}
package com.isoftstone.cloud.security.handler;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.MediaType;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
@Component
public class LoginHandler implements LogoutSuccessHandler {
@Override
public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException {
httpServletResponse.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
HashMap hashMap = new HashMap();
hashMap.put("code", 200);
hashMap.put("data", null);
hashMap.put("msg", "退出成功");
httpServletResponse.getWriter().write(new ObjectMapper().writeValueAsString(hashMap));
}
}
package com.isoftstone.cloud.security.handler;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.MediaType;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
@Component
public class NoAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
httpServletResponse.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
HashMap hashMap = new HashMap();
hashMap.put("code", 403);
hashMap.put("data", null);
hashMap.put("msg", "访问接口无权限");
httpServletResponse.getWriter().write(new ObjectMapper().writeValueAsString(hashMap));
}
}
package com.isoftstone.cloud.security.handler;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.isoftstone.cloud.vo.UserVO;
import org.springframework.http.MediaType;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
@Component
public class SuccessHandler implements AuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
httpServletResponse.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
UserVO userVo = new UserVO();
userVo.setToken("sakldgjklasdjglaslknzxS216DG416AS1DG651AS6D5G16AS");
userVo.setNikName("oauth2.0测试");
HashMap hashMap = new HashMap();
hashMap.put("code", 200);
hashMap.put("data", userVo);
hashMap.put("msg", "登录成功");
httpServletResponse.getWriter().write(new ObjectMapper().writeValueAsString(hashMap));
}
}
自定义客户端详情service
package com.isoftstone.cloud.security.service;
import com.alibaba.fastjson.JSON;
import com.isoftstone.cloud.pojo.PermissionEntity;
import com.isoftstone.cloud.pojo.UserEntity;
import com.isoftstone.cloud.service.PermissionService;
import com.isoftstone.cloud.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.util.List;
import java.util.stream.Collectors;
@Service
@Slf4j
public class SecurityUserDetailService implements UserDetailsService {
@Autowired
private UserService userService;
@Autowired
private PermissionService permissionService;
@Override
public UserDetails loadUserByUsername(String username) {
UserEntity user = userService.getUserByUsername(username);
if (user == null) {
return null;
}
//获取权限
List<PermissionEntity> permissions = permissionService.getPermissionsByUserId(user.getId());
List<String> codes = permissions.stream().map(PermissionEntity::getCode).collect(Collectors.toList());
String[] authorities = null;
if (!CollectionUtils.isEmpty(codes)) {
authorities = new String[codes.size()];
codes.toArray(authorities);
}
//身份令牌
String principal = JSON.toJSONString(user);
return User.withUsername(principal).password(user.getPassword()).authorities(authorities).build();
}
}
securtiy资源服务
引入依赖
<!--授权-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
配置资源服务
package com.isoftstone.cloud.security.config;
import com.isoftstone.cloud.security.handler.OAuthResourceAuthenticationEntryPoint;
import com.isoftstone.cloud.security.handler.CustomAccessDeniedHandler;
import org.springframework.beans.factory.annotation.Autowired;
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.http.SessionCreationPolicy;
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.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true,jsr250Enabled = true,securedEnabled = true)
public class ResouceServerConfig extends ResourceServerConfigurerAdapter {
private static final String RESOURCE_ID="res1";
@Autowired
private TokenStore tokenStore;
@Autowired
private JwtAccessTokenConverter jwtAccessTokenConverter;
@Autowired
private CustomAccessDeniedHandler customAccessDeniedHandler;
@Autowired
private OAuthResourceAuthenticationEntryPoint authenticationEntryPoint;
/**
* 令牌服务的配置
*/
@Bean
public ResourceServerTokenServices tokenServices() {
DefaultTokenServices services = new DefaultTokenServices();
//配置令牌存储策略,使用AccessTokenConfig配置的JwtTokenStore
services.setTokenStore(tokenStore);
//令牌的增强JwtAccessTokenConverter
services.setTokenEnhancer(jwtAccessTokenConverter);
return services;
}
/**
* 配置资源id和令牌校验服务
*/
@Override
public void configure(ResourceServerSecurityConfigurer resources) {
//配置唯一资源id
resources.resourceId(RESOURCE_ID)
//定制令牌失效的提示信息
.authenticationEntryPoint(authenticationEntryPoint)
//定制权限不足的提示信息
.accessDeniedHandler(customAccessDeniedHandler)
//配置令牌校验服务
.tokenServices(tokenServices());
}
/**
* 配置security的安全机制
*/
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/customer/**").access("#oauth2.hasScope('ROLE_ADMIN')")
.and().csrf().disable()
//session禁用,因为使用了token
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
配置token管理类
package com.isoftstone.cloud.security.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;
@Configuration
public class TokenConfig {
/**
* 密钥串
*/
private static final String SIGNING_KEY="uaa";
@Bean
public TokenStore tokenStore(){
return new JwtTokenStore(jwtAccessTokenConverter());
}
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter(){
JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
jwtAccessTokenConverter.setSigningKey(SIGNING_KEY);
return jwtAccessTokenConverter;
}
}
token解析过滤器
package com.isoftstone.cloud.security.filter;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.isoftstone.cloud.security.model.JwtUser;
import com.isoftstone.cloud.util.EncryptUtil;
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.util.StringUtils;
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;
@Component
public class AuthenticationFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
String token = request.getHeader("token");
if (!StringUtils.isEmpty(token)){
String json = EncryptUtil.decodeUTF8StringBase64(token);
JSONObject jsonObject = JSON.parseObject(json);
//获取用户身份信息、权限信息
String principal = jsonObject.getString("principal");
JwtUser user = JSON.parseObject(principal, JwtUser.class);
JSONArray tempJsonArray = jsonObject.getJSONArray("authorities");
String[] authorities = tempJsonArray.toArray(new String[0]);
//身份信息、权限信息填充到用户身份token对象中
UsernamePasswordAuthenticationToken authenticationToken=new UsernamePasswordAuthenticationToken(user,null,
AuthorityUtils.createAuthorityList(authorities));
//创建details
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
//将authenticationToken填充到安全上下文
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
}
filterChain.doFilter(request,response);
}
}
自定义未授权处理类和端点权限不足处理类
package com.isoftstone.cloud.security.handler;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.MediaType;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
@Component
public class CustomAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
httpServletResponse.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
HashMap hashMap = new HashMap();
hashMap.put("code", 403);
hashMap.put("data", null);
hashMap.put("msg", "访问接口无权限");
httpServletResponse.getWriter().write(new ObjectMapper().writeValueAsString(hashMap));
}
}
package com.isoftstone.cloud.security.handler;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
@Component
public class OAuthResourceAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {
HashMap<String, Object> map = new HashMap<>();
map.put("code", 403);
map.put("msg", "未进行授权,暂时无法访问");
map.put("uri", request.getRequestURI());
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
response.setCharacterEncoding("utf-8");
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
ObjectMapper objectMapper = new ObjectMapper();
String resBody = objectMapper.writeValueAsString(map);
PrintWriter printWriter = response.getWriter();
printWriter.print(resBody);
printWriter.flush();
printWriter.close();
}
}
网关整合securtiy
引入依赖
<!--授权-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
过滤器token解析和向下游传递
package com.isoftstone.cloud.config.security;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.isoftstone.cloud.util.EncryptUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.ServerHttpRequest;
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.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Component
@Slf4j
public class GatewayFilterConfig implements GlobalFilter, Ordered {
private Logger logger= LoggerFactory.getLogger("GLOBAL-GATEWAY-FLOW");
@Autowired
private TokenStore tokenStore;
@SneakyThrows
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String requestUrl = exchange.getRequest().getPath().value();
AntPathMatcher pathMatcher = new AntPathMatcher();
//1 uaa服务所有放行
if (pathMatcher.match("/auth/**", requestUrl)) {
return chain.filter(exchange);
}
//2 检查token是否存在
String token = getToken(exchange);
if (StringUtils.isEmpty(token)) {
return noTokenMono(exchange);
}
logger.info("请求进入");
//3 判断是否是有效的token
OAuth2AccessToken oAuth2AccessToken;
try {
oAuth2AccessToken = tokenStore.readAccessToken(token);
Map<String, Object> additionalInformation = oAuth2AccessToken.getAdditionalInformation();
//取出用户身份信息
String principal = (String)additionalInformation.get("user_name");
//获取用户权限
List<String> authorities = (List<String>) additionalInformation.get("authorities");
HashMap hashMap = new HashMap();
hashMap.put("principal",principal);
hashMap.put("authorities",authorities);
//给header里面添加值
String base64 = EncryptUtil.encodeUTF8StringBase64(new ObjectMapper().writeValueAsString(hashMap));
ServerHttpRequest tokenRequest = exchange.getRequest().mutate().header("token", base64).build();
ServerWebExchange build = exchange.mutate().request(tokenRequest).build();
return chain.filter(build);
} catch (InvalidTokenException e) {
log.info("无效的token: {}", token);
return invalidTokenMono(exchange);
}
}
/**
* 获取token
*/
private String getToken(ServerWebExchange exchange) {
String tokenStr = exchange.getRequest().getHeaders().getFirst("Authorization");
if (StringUtils.isEmpty(tokenStr)) {
return null;
}
String token = tokenStr.split(" ")[1];
if (StringUtils.isEmpty(token)) {
return null;
}
return token;
}
/**
* 无效的token
*/
private Mono<Void> invalidTokenMono(ServerWebExchange exchange) throws IOException {
HashMap hashMap = new HashMap();
hashMap.put("status", HttpStatus.UNAUTHORIZED.value());
hashMap.put("msg", "无效的token");
return buildReturnMono(hashMap, exchange);
}
private Mono<Void> noTokenMono(ServerWebExchange exchange) throws IOException {
HashMap hashMap = new HashMap();
hashMap.put("status", HttpStatus.UNAUTHORIZED.value());
hashMap.put("msg", "没有token");
return buildReturnMono(hashMap, exchange);
}
private Mono<Void> buildReturnMono(HashMap hashMap, ServerWebExchange exchange) throws IOException {
ServerHttpResponse response = exchange.getResponse();
byte[] bits = new ObjectMapper().writeValueAsString(hashMap).getBytes(StandardCharsets.UTF_8);
DataBuffer buffer = response.bufferFactory().wrap(bits);
response.setStatusCode(HttpStatus.UNAUTHORIZED);
//指定编码,否则在浏览器中会中文乱码
response.getHeaders().add("Content-Type", "text/plain;charset=UTF-8");
return response.writeWith(Mono.just(buffer));
}
@Override
public int getOrder() {
return 0;
}
}
token管理类
package com.isoftstone.cloud.config.security;
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;
@Configuration
public class TokenConfig {
/**
* 秘钥串
*/
private static final String SIGNING_KEY = "uaa";
@Bean
public TokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
}
@Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey(SIGNING_KEY);
return converter;
}
}
web安全配置类
package com.isoftstone.cloud.config.security;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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 SecurityConfig {
@Bean
public SecurityWebFilterChain webFluxSecurityFilterChain(ServerHttpSecurity http) {
return http.csrf().disable()
.authorizeExchange()
.pathMatchers("/**").permitAll()
.anyExchange().authenticated()
.and()
.build();
}
}
使用说明
获取token

带着token请求微服务接口
