java技术:oauth2协议

news2024/11/24 5:33:26

目录

一、黑马程序员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;
//    }

    @Bean
    public PasswordEncoder passwordEncoder() {
//        //密码为明文方式
//        return NoOpPasswordEncoder.getInstance();
        //spring用于加密的一个算法
        //授权码模式必须是加密形式
        return new BCryptPasswordEncoder();
    }

    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    //安全拦截机制(最重要)
    @Override
    protected 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存储方式
    @Bean
    public TokenStore tokenStore() {
        return new JwtTokenStore(accessTokenConverter());
    }

    //定义jwt校验
    @Bean
    public 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

    @Override
    public 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 {

    @Autowired
    UserMapper userMapper;
    @Override
    public 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{

    @Autowired
    AuthenticationManager authenticationManager;

    @Autowired
    UserMapper userMapper;
    @Autowired
    RedisTemplateUtils redisTemplateUtils;
    //authenticationManager在login的方法(这里是实现类)调用,就继续传递
    @Override
    public 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);
        //获取返回中的信息 加密返回token
        HashMap<String, Object> claims = new HashMap<>();
        LoginUser principal = (LoginUser)authenticate.getPrincipal();
        claims.put("userId",principal.getUser().getId());
        String token = generateJwt(claims);
        //将token存入redis 并以userId为key
        redisTemplateUtils.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);
    }

    @Override
    public Result loginOut() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        LoginUser principal = (LoginUser)authentication.getPrincipal();
        String userId = principal.getUser().getId();
        //删除redis中的token
        try {
            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";

    @Autowired
    TokenStore tokenStore;

    @Autowired
    TokenAuthenticationFilter tokenAuthenticationFilter;
    //服务资源配置
    @Override
    public void configure(ResourceServerSecurityConfigurer resources) {
        resources.resourceId(RESOURCE_ID)//资源 id
//                .tokenServices(tokenService())
                .tokenStore(tokenStore)
                .stateless(true);
    }
//服务端访问控制
    @Override
    public 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();
//    }

    @Autowired
    private JwtAccessTokenConverter accessTokenConverter;

    @Bean
    public TokenStore tokenStore() {
        return new JwtTokenStore(accessTokenConverter());
    }

    @Bean
    public 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
    @EnableResourceServer
    public class UAAServerConfig extends
            ResourceServerConfigurerAdapter {

        @Autowired
        private TokenStore tokenStore;

        @Override
        public void configure(ResourceServerSecurityConfigurer resources){
            resources.tokenStore(tokenStore).resourceId(RESOURCE_ID)
                    .stateless(true);
        }

        @Override
        public void configure(HttpSecurity http) throws Exception {
            http.authorizeRequests()
                    .antMatchers("/uaa/**").permitAll();
        }

    }

    /**
     *  订单服务
     */
    @Configuration
    @EnableResourceServer
    public class OrderServerConfig extends
            ResourceServerConfigurerAdapter {
        @Autowired
        private TokenStore tokenStore;

        @Override
        public void configure(ResourceServerSecurityConfigurer resources) {
            resources.tokenStore(tokenStore).resourceId(RESOURCE_ID)
                    .stateless(true);
        }
        @Override
        public 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存储方式
    @Bean
    public TokenStore tokenStore() {
        return new JwtTokenStore(accessTokenConverter());
    }

    //定义jwt校验
    @Bean
    public 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 {


    //安全拦截配置
    @Bean
    public 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{
    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse
            httpServletResponse, 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.解析token
            String 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( new
                    String[authoritiesArray.size()]);
//            2.新建并填充authentication
            UsernamePasswordAuthenticationToken authentication = new
                    UsernamePasswordAuthenticationToken(
                    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();
        }


    }

    @Autowired
    private TokenStore tokenStore;


    @Override
    public 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);
        }
        //判断是否是有效的token
        OAuth2AccessToken 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));
    }


    @Override
    public int getOrder() {
        return 0;
    }
}

未完!

有关springsecurity单独实现认证授权

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1686084.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

linux---进程通信

提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、匿名管道 进程之间的通信目的一般是来控制另一个进程。也可以用来实现数据的交流&#xff0c;还有资源共享等。 匿名管道原理&#xff1a; &#xff08;铺垫&#xff09;进程之间是具有独立性&…

mysql实战——xtrabackup全量备份/增量备份及恢复

一、测试前准备 mysql数据库 端口3306数据文件目录 /data/mysql/3306/data 安装目录/usr/lcoal/mysql配置文件/etc/my.cnf 创建数据库 testXtra 创建备份目录 备份目录/data/backup/备份恢复数据文件目录/data/mysql/3307/data备份恢复配置文件/etc/my_3307.cnf 二、开始…

宠物医院兽医电子处方管理系统软件操作教程,佳易王兽医处方软件分享

试用超级版宠物医院兽医处方管理系统软件V17.3&#xff0c;软件功能实用&#xff0c;操作简单。试用版免费试用&#xff0c;技术支持可以联系客服。 一、软件下载说明及软件操作教程链接 软件下载请点击最下方官网卡片进入 1、软件下载注意事项 【特别说明&#xff1a;下载的…

比亚迪一4S店着火:浅述烟火识别技术与消防安全预警方案的必要性

据新闻报道&#xff0c;2024年5月16日&#xff0c;福建福州一家比亚迪4S店发生火灾。事发后&#xff0c;当地消防立即调员赶往现场救援&#xff0c;大火导致展厅展车基本烧毁&#xff0c;部分维修车辆受损&#xff0c;没有人员伤亡。 随着汽车市场的不断扩大&#xff0c;4S店作…

web自动化文件上传弹框处理

目录 文件上传介绍文件上传处理Alert 弹窗介绍Alert 弹窗处理 课程目标 掌握文件上传的场景以及文件上传的处理方式。掌握 Alert 弹窗的场景以及 Alert 弹窗的处理方式。 思考 碰到需要上传文件的场景&#xff0c;自动化测试应该如何解决&#xff1f; 文件上传处理 找到文…

linux中的arch命令使用

arch 显示当前主机的硬件架构类型 概要 arch [OPTION]...主要用途 打印机器架构信息&#xff1b;arch 命令输出结果有&#xff1a;i386、i486、i586、alpha、sparc、arm、m68k、mips、ppc、i686等。 选项 --help 显示帮助信息并退出。 --version 显示版本信息并…

华为大咖说 | 企业应用AI大模型的“道、法、术” ——道:认知篇

本文作者&#xff1a;郑岩&#xff08;华为云AI变革首席专家&#xff09;全文约3313字&#xff0c;阅读约需8分钟&#xff0c;请仔细看看哦~ 前阵子&#xff0c;我在公司内部发了一篇Sora的科普贴&#xff0c;本来只是个简单的技术总结&#xff0c;但让我意外的是&#xff0c;…

pyqt5与yolov5进行视频检测(一)——登录操作

项目效果展示 一、登录界面 二、主界面 目前在更新中。。。 一、设计 二、登录代码 注意&#xff1a;下面会导入主界面的包&#xff0c;图片资源自己设计一下&#xff0c;密码保存时没设计加密&#xff0c;需要自行设计 main_window主界面下文会设计from main_window impor…

新《体外诊断试剂分类目录》已发布,企业应该如何应对?

除了新版《体外诊断试剂分类目录》的变化外(见&#xff1a;体外诊断试剂分类目录的变与不变)&#xff0c;最值得关心的是&#xff1a;- 2024版《体外诊断试剂分类目录》发布以后&#xff0c;过渡期多久&#xff1f;- 当前分类目录背景下如何管理&#xff1f;- 对在研、已取证、…

编程学习 (C规划) 6 {24_4_14} 六 ( 函数 )

1.函数的概念 C语言中函数就是一个子程序&#xff0c;C语言中的函数就是一个完成某项特定任务的一小段代码&#xff0c;有特殊的写法哥调用方法。 C语言中一般会见到两种函数 &#xff08;1&#xff09;库函数 &#xff08;2&#xff09;自定义函数 2.库函数 其实就是C语…

【MATLAB】基于VMD-SSA-LSTM的回归预测模型

有意向获取代码&#xff0c;请转文末观看代码获取方式~ 1 基本定义 基于VMD-SSA-LSTM的回归预测模型是一种结合了多种时间序列分析和机器学习技术的综合模型。下面我将分别介绍这三个组成部分的基本原理&#xff0c;并解释它们是如何结合起来进行回归预测的。 变分模态分解&a…

VMware Workstation 虚拟机安装 ubuntu 24.04 server 详细教程 服务器安装图形化界面

1 阿里云下载 ubuntu-releases安装包下载_开源镜像站-阿里云 2 打开vmware,新建虚拟机 3 选择下载的镜像,开始安装 3 光驱这里修改下 4 重新启动&#xff0c;安装图形化界面 #更新软件包列表 sudo apt-get update #安装Ubuntu图形桌面 sudo apt install ubuntu-desktop 5 安…

Helm安装kafka3.7.0无持久化(KRaft 模式集群)

文章目录 2.1 Chart包方式安装kafka集群 5.开始安装2.2 命令行方式安装kafka集群 搭建 Kafka-UI三、kafka集群测试3.1 方式一3.2 方式二 四、kafka集群扩容4.1 方式一4.2 方式二 五、kafka集群删除 参考文档 [Helm实践---安装kafka集群 - 知乎 (zhihu.com)](https://zhuanlan.…

《黑龙江日报》是什么级别的报刊?

《黑龙江日报》是什么级别的报刊&#xff1f; 《黑龙江日报》是省级党报。它是黑龙江省重要的主流媒体之一&#xff0c;在传达党的方针政策、反映社会动态、推动地方发展等方面发挥着重要作用。具有较高的权威性和影响力&#xff0c;为黑龙江省的新闻传播和信息交流做出了重要…

云动态摘要 2024-05-24

给您带来云厂商的最新动态&#xff0c;最新产品资讯和最新优惠更新。 最新优惠与活动 [免费试用]大模型知识引擎体验招募 腾讯云 2024-05-21 大模型知识引擎产品全新上线&#xff0c;为回馈新老客户&#xff0c;50万token免费送&#xff0c;开通服务即领取&#xff01; 云服…

清空了电脑回收站,之前的文件还能否恢复?

电脑已成为我们日常生活中不可或缺的一部分。我们在电脑上处理文档、保存图片、下载视频等&#xff0c;而电脑中的回收站则成为我们处理不再需要文件的一个便捷工具&#xff0c;当我们想要删除某些文档的话&#xff0c;它并不是立即从硬盘上消失&#xff0c;而是被系统移动到了…

SwanLab入门深度学习:BERT IMDB文本情感分类

基于BERT模型的IMDB电影评论情感分类&#xff0c;是NLP经典的Hello World任务之一。 这篇文章我将带大家使用SwanLab、transformers、datasets三个开源工具&#xff0c;完成从数据集准备、代码编写、可视化训练的全过程。 观察了一下&#xff0c;中文互联网上似乎很少有能直接…

光敏聚酰亚胺(PSPI)行业技术壁垒较高 本土企业已具备相关产品量产能力

光敏聚酰亚胺&#xff08;PSPI&#xff09;行业技术壁垒较高 本土企业已具备相关产品量产能力 光敏聚酰亚胺&#xff08;PSPI&#xff09;又称光敏PI、光刻胶用聚酰亚胺&#xff0c;指将光敏基团引入聚酰亚胺分子链中制成的高性能有机材料。PSPI拥有极佳耐热性、化学稳定性、热…

新书推荐:6.1 if语句

计算机语言和人类语言类似&#xff0c;人类语言是为了解决人与人之间交流的问题&#xff0c;而计算机语言是为了解决程序员与计算机之间交流的问题。程序员编写的程序就是计算机的控制指令&#xff0c;控制计算机的运行。借助于编译工具&#xff0c;可以将各种不同的编程语言的…

Jenkins工具系列 —— 通过钉钉API 发送消息

文章目录 钉钉环境搭建使用钉钉API接口 发送消息机器人安全设置使用自定义关键词机器人安全设置使用加签方式 资料下载 钉钉环境搭建 在jenkins安装钉钉插件以及小机器人&#xff0c;这部分内容可参考&#xff1a;插件 钉钉发送消息 使用钉钉API接口 发送消息 机器人安全设置…