• Spring Security oauth2.0 客户端


    引入依赖

    pom 文件配置

    • spring-boot:选用 2.6.4 版本,2.7.x 后不再维护 oauth 模块
    • security:引入安全策略,不需固定版本
    • oauth:引入 oauth 2.0 相关类,选用 2.2.7.RELEASE,后续版本有很多类被过期
    • jwt:引入 jwt 相关类,选用最新的版本
    • jaxb:若 jdk 为 8 则不需引入,升到 11 后必须引入
    <parent>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-parentartifactId>
        <version>2.6.4version>
        <relativePath/>
    parent>
    
    <dependency>
         <groupId>org.springframework.bootgroupId>
         <artifactId>spring-boot-starter-securityartifactId>
     dependency>
    
     <dependency>
         <groupId>org.springframework.security.oauthgroupId>
         <artifactId>spring-security-oauth2artifactId>
         <version>2.3.2.RELEASEversion>
     dependency>
    
     <dependency>
         <groupId>org.springframework.securitygroupId>
         <artifactId>spring-security-jwtartifactId>
         <version>1.1.1.RELEASEversion>
     dependency>
    
     <dependency>
         <groupId>org.glassfish.jaxbgroupId>
         <artifactId>jaxb-runtimeartifactId>
         <version>2.3.6version>
     dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

     

    配置资源服务器

    oauth 2.0 服务端可以参考文章: Spring Security oauth2.0 服务端

    建议 token 使用 JWT,JWT 可以使用算数的方法进行校验,不需要对颁发 token 的服务端发起验证请求

     

    配置 JWT

    • JwtAccessTokenConverter:配置 JWT 转换器
    • signKey:JWT 的签名,需要和生成 JWT 的服务端一致才能校验通过
    • TokenStore:token 的持久化方式,使用 JWT 即使用算数方式,不涉及存储;添加 converter,即添加 JWT 的解析参数
    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey(signKey);
        return converter;
    }
    
    @Bean
    public TokenStore tokenStore() {
        return new JwtTokenStore(jwtAccessTokenConverter());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

     

    客户端安全配置

    • ResourceServerConfigurerAdapter:配置类必须继承此 Adapter
    • @EnableResourceServe: OAuth2 资源服务器提供方便的注释,开启后会经过 Spring Security 过滤器,对传入的 OAuth2 令牌对请求进行身份验证
    • resources:添加定义好的 tokenStore,添加 resourceId
    • resourceId:服务端生成 JWT 时的一个参数,需对应上才能校验成功
    • HttpSecurity:配置哪些 url 请求可以不经过安全校验
    @Configuration
    @EnableResourceServer
    public class ResourceServerConfigurer extends ResourceServerConfigurerAdapter {
    
        private static String resourceId = "oauth2-resource";
    
        private static List<String> ignoreUrl = new ArrayList<>();
    
        @Override
        public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
            super.configure(resources);
            resources.tokenStore(tokenStore());
            resources.resourceId(resourceId);
        }
    
        @Override
        public void configure(HttpSecurity http) throws Exception {
            List<String> ignoreUrl = new ArrayList<>();
            ignoreUrl.add("/test");
            String[] ignoreUrls = ignoreUrl.toArray(new String[ignoreUrl.size()]);
            
            http.authorizeRequests()
            		.antMatchers(ignoreUrls).permitAll() 
                    .anyRequest().authenticated();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

     

    获取 JWT 内容

    默认转换器 DefaultUserAuthenticationConverter,JWT 返回的是 user_name 的值,代码如下所示

    public class DefaultUserAuthenticationConverter implements UserAuthenticationConverter {
    	
    	...
    	
    	public Authentication extractAuthentication(Map<String, ?> map) {
    		if (map.containsKey(USERNAME)) {
    			Object principal = map.get(USERNAME);
    			Collection<? extends GrantedAuthority> authorities = getAuthorities(map);
    			if (userDetailsService != null) {
    				UserDetails user = userDetailsService.loadUserByUsername((String) map.get(USERNAME));
    				authorities = user.getAuthorities();
    				principal = user;
    			}
    			return new UsernamePasswordAuthenticationToken(principal, "N/A", authorities);
    		}
    		return null;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    通过 SecurityContextHolder 可以获取

        @GetMapping("/test/username")
        public Result<Object> getUserName() throws Exception {
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); 
            return Result.success("成功!", authentication);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5

     

    获取 JWT 实体

    通过上述已知,默认情况 JWT 只返回用户的用户名,但实际情况我们希望返回服务端传入 JWT 的实体,即实现 UserDetails 接口的类

    自定义用户身份验证转换器

    • 自定义转换器,继承 DefaultUserAuthenticationConverter
    • 在 UsernamePasswordAuthenticationToken 赋值 principal 为 User 实体
    • User 是 org.springframework.security.core.userdetails 的实现类
    public class CustomUserAuthenticationConverter extends DefaultUserAuthenticationConverter {
    
        @Override
        public Authentication extractAuthentication(Map<String, ?> map) {
                String userName = (String) map.get(USERNAME);
                User principal = new User( userName,"N/A", new ArrayList<>());
                return new UsernamePasswordAuthenticationToken(principal, "N/A", new ArrayList<>());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    把刚新建的转换器添加到 JwtAccessTokenConverter 里

    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter() {
        CustomUserAuthenticationConverter userAuthenticationConverter = new CustomUserAuthenticationConverter();
        DefaultAccessTokenConverter accessTokenConverter = new DefaultAccessTokenConverter();
        accessTokenConverter.setUserTokenConverter(userAuthenticationConverter);
    
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey(signKey);
        converter.setAccessTokenConverter(accessTokenConverter);
        return converter;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    获取实体

    • User 是 org.springframework.security.core.userdetails 的实现类
        @GetMapping("/test/username")
        public Result<Object> getUserName() throws Exception {
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            User user = (User) authentication.getPrincipal();
            return Result.success("成功!", authentication);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
  • 相关阅读:
    在 Android 10 中访问/proc/net/route权限被拒绝
    Qt学习总结之QTextEdit
    2022“杭电杯”中国大学生算法设计超级联赛(2)(持续更新)
    树相关——树链剖分(轻重链剖分)
    网络安全运维工程师(NISP-SO)需要掌握那些知识点
    2.15 这样的小红书图片内容,最容易“踩雷”!【玩赚小红书】
    5- FreeRTOS任务通知
    el-table固定表头(设置height)出现内容过多时不能滚动问题
    传统行业+NFT然后迎来新增长点?
    Flink系列之Flink中StateBackend深入剖析和应用
  • 原文地址:https://blog.csdn.net/weixin_42555971/article/details/127566062