栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Springcloud+security+oauth2+网关

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Springcloud+security+oauth2+网关

网关整合 OAuth2.0 有两种思路,一种是授权服务器生成令牌, 所有请求统一在网关层验证,判断
权限等操作;另一种是由各资源服务处理,网关只做请求转发。 比较常用的是第一种,把API网
关作为OAuth2.0的资源服务器角色,实现接入客户端权限拦截、令牌解析并转发当前登录用户信
息给微服务,这样下游微服务就不需要关心令牌格式解析以及OAuth2.0相关机制了。
网关在认证授权体系里主要负责两件事:
(1)作为OAuth2.0的资源服务器角色,实现接入方权限拦截。
(2)令牌解析并转发当前登录用户信息(明文token)给微服务
微服务拿到明文token(明文token中包含登录用户的身份和权限信息)后也需要做两件事:
(1)用户授权拦截(看当前用户是否有权访问该资源)
(2)将用户信息存储进当前线程上下文(有利于后续业务逻辑随时获取当前用户信息)

直接上手 授权中心 my-auth-server

(作用:获取token 验证token)
授权配置
(密码模式)
采用redis存token
密钥和appId存在数据库

DROP TABLE IF EXISTS `oauth_client_details`;
CREATE TABLE `oauth_client_details`  (
  `client_id` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `resource_ids` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `client_secret` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `scope` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `authorized_grant_types` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `web_server_redirect_uri` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `authorities` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `access_token_validity` int(0) NULL DEFAULT NULL,
  `refresh_token_validity` int(0) NULL DEFAULT NULL,
  `additional_information` varchar(4096) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `autoapprove` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`client_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of oauth_client_details
-- ----------------------------
INSERT INTO `oauth_client_details` VALUES ('appId', 'mayikt_resource', '$2a$10$fBM0guMoKWwZye6u7OAeWuguHL.ElffCe6KQfZsFX44JdQ4gsEDEa', 'all', 'authorization_code,password,client_credentials,refresh_token', 'http://www.mayikt.com/callback', NULL, NULL, NULL, NULL, NULL);

授权配置

@Component
@EnableAuthorizationServer
public class AuthorizationConfig extends AuthorizationServerConfigurerAdapter {
    @Autowired
    private PasswordEncoder passwordEncoder;
    @Autowired
    private AuthenticationManager authenticationManagerBean;
    @Autowired
    private UserService userService;
    @Autowired
    private TokenStore tokenStore;
    @Autowired
    private DataSource dataSource;


    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.authenticationManager(authenticationManagerBean) //使用密码模式需要配置
                .reuseRefreshTokens(false) refresh_token是否重复使用
                .tokenStore(tokenStore)
                .userDetailsService(userService)  //refresh_token是否重复使用
                .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST); //支持GET,POST请求
    }

    //token http://localhost:8884/oauth/token?username=admin&password=123456&grant_type=password&client_id=appId&client_secret=123456&scope=all
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        //允许表单提交
        security.allowFormAuthenticationForClients()
                .checkTokenAccess("permitAll()");
    }


    
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        
//内存模式
//        clients.inMemory()
//                // appid 表里取 这里写死
//                .withClient("appId")
//                // 密钥  表里取 这里写死
//                .secret(passwordEncoder.encode("123456"))
//                // 授权码
//                .authorizedGrantTypes("authorization_code","password","client_credentials","refresh_token")
//                // 作用域
//                .scopes("all")
//                // 资源的id  表里取 这里写死
//                .resourceIds("mayikt_resource")
//                // 回调地址  表里取 这里写死
//                .redirectUris("http://www.mayikt.com/callback");
//数据库模式

        clients.withClientDetails(clientDetails());
    }

    @Bean
    public ClientDetailsService clientDetails() {
        //读取oauth_client_details表
        return new JdbcClientDetailsService(dataSource);
    }

}

获取token和刷新token url

获取 token  http://localhost:8884/oauth/token?username=admin&password=123456&grant_type=password&client_id=appId&client_secret=123456&scope=all
刷新密码 http://localhost:8080/oauth/token?grant_type=refresh_token&client_id=appId&client_secret=123456&refresh_token=1b46f93f-af95-4ce6-afae-618eca676ebc

security配置

@Component
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }


    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin().permitAll()
                .and().authorizeRequests()
                .antMatchers("/oauth
@Component
@Order(0) //数值越小优先级越高
public class AuthenticationFilter implements GlobalFilter, InitializingBean {

    private static Set noFilterUrl = new linkedHashSet<>();

    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private RedisTemplate redisTemplate;


    
    @Override
    public void afterPropertiesSet() throws Exception {
        // 不拦截认证的请求
        noFilterUrl.add("/oauth/token");
        noFilterUrl.add("/oauth/checkToken");
        noFilterUrl.add("/user/index");
        noFilterUrl.add("/user/login");
    }

    @Override
    public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        String requestPath = request.getURI().getPath();

        //不需要认证的url
        if (isContinsUrl(requestPath)) {
            return chain.filter(exchange);
        }

        //String token = exchange.getRequest().getQueryParams().getFirst("token");
        //获取请求头

        String authHeader = request.getHeaders().getFirst("Authorization");
        //请求头为空
        if (StringUtils.isEmpty(authHeader)) {
            throw new RuntimeException("请求头为空");
        }

        //校验token
        TokenInfo tokenInfo = null;
        try {
            tokenInfo = getTokenInfo(authHeader);
        } catch (Exception e) {
            throw new RuntimeException("token校验报错");
        }
        //将token信息带过去
        exchange.getAttributes().put("tokenInfo", tokenInfo);
        return chain.filter(exchange);
    }


    private boolean isContinsUrl(String reqPath) {
        for (String skipPath : noFilterUrl) {
            if (reqPath.contains(skipPath)) {
                return true;
            }
        }
        return false;
    }

    
    public TokenInfo getTokenInfo(String authHeader) {
        // 获取token的值
        String token = StringUtils.substringAfter(authHeader, "bearer ");
        TokenInfo tokenInfo = null;
        //校验token
        String tokenUrl = "http://my-auth/oauth/checkToken?token=" + token;
        tokenInfo = restTemplate.getForObject(tokenUrl, TokenInfo.class);
        //校验成功后将信息存入redis
        String username = tokenInfo.getUser_name();
        redisTemplate.opsForValue().setIfAbsent(username, JSON.toJSONString(tokenInfo),30L, TimeUnit.MINUTES);
        System.out.println("==========redisTemplate===========" + redisTemplate.opsForValue().get(username));
        return tokenInfo;
    }

}
@Component
@Order(1)
public class AuthorizationFilter implements GlobalFilter, InitializingBean {


    private static Set noFilterUrl = new linkedHashSet<>();
    @Autowired
    private RoleMapper roleMapper;


    @Override
    public void afterPropertiesSet() throws Exception {
        // 不拦截认证的请求
        // 不拦截认证的请求
        noFilterUrl.add("/oauth/token");
        noFilterUrl.add("/oauth/checkToken");
        noFilterUrl.add("/user/index");
        noFilterUrl.add("/user/login");

    }

    @Override
    public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        String requestPath = exchange.getRequest().getURI().getPath();
        //不需要认证的url
        if (shouldSkip(requestPath)) {
            return chain.filter(exchange);
        }
        //从上个过滤器获取信息tokenInfo
        TokenInfo tokenInfo = exchange.getAttribute("tokenInfo");

        if (!tokenInfo.isActive()) {
            throw new RuntimeException("token过期");
        }

        hasPremisson(tokenInfo, requestPath);

        return chain.filter(exchange);
    }

    private boolean shouldSkip(String reqPath) {

        for (String skipPath : noFilterUrl) {
            if (reqPath.contains(skipPath)) {
                return true;
            }
        }
        return false;
    }

    
    private void hasPremisson(TokenInfo tokenInfo, String currentUrl) {
        boolean hasPremisson = false;

        List premessionList = Arrays.asList(tokenInfo.getAuthorities());
        //获取登陆用户角色权限有哪些url
        Set urlList = new linkedHashSet<>();
        for (String premession : premessionList) {
            List userRoles = roleMapper.selectUserRoleByenname(premession);
            userRoles.stream().forEach(item -> {
                urlList.add(item.getUrl());
            });
        }

        //判断角色权限url 是否包含当前url
        for (String u : urlList) {
            if (u.equals(currentUrl)) {
                hasPremisson = true;
            }
        }

        if (!hasPremisson) {
            throw new RuntimeException("没有权限");
        }
    }


}

yml

server:
  port: 9999

spring:
  application:
    name: my-gataway
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.174.10:8848

    gateway:
      discovery:
        locator:
          lower-case-service-id: true  #微服务名称小写
          enabled: true   # 打开后只要注册过的服务可以通过服务名转发(
      #路由
      routes:
        - id: routes_1
          uri: lb://my-login
          predicates:
            - Path=/user/**

        - id: routes_2
          uri: lb://my-producter-servcer
          predicates:
            - Path=/product/**


  datasource:
    username: root
    password: 123456
    url: jdbc:mysql://192.168.174.10:3306/oauth2-sso?serverTimezone=GMT%2B8
    driver-class-name: com.mysql.jdbc.Driver

  redis:
    database: 0
    host: 127.0.0.1
    port: 6379


mybatis:
  mapper-locations: classpath:mapping/*.xml
  type-aliases-package: com.huawei.dao
  configuration:  #开启sql打印
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

源码地址:https://gitee.com/zhu_can_admin/spring-security-oauth2-gateway.git

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/600897.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号