• SpringSecurity Oauth2实战 - 03 内存数据源完成认证登录


    1. 搭建资源服务器

    搭建资源服务器和授权服务器我前面文章已经讲过,资源服务器是一个starter,这里就不重复了。

    1. 密码加密配置类 PasswordEncodeConfig
    @Configuration
    public class PasswordEncodeConfig {
        @Bean
        public PasswordEncoder passwordEncoder() {
            return new BCryptPasswordEncoder();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    2. Token存储配置类 TokenStoreAutoConfiguration
    @Configuration
    public class TokenStoreAutoConfiguration {
    
        @Autowired
        private RedisConnectionFactory connectionFactory;
    
        /**
         * 初始化 RedisTokenStore 用于将 token 存储至 Redis
         */
        @Bean
        public RedisTokenStore redisTokenStore() {
            RedisTokenStore redisTokenStore = new RedisTokenStore(connectionFactory);
            // 设置key的层级前缀,方便查询
            redisTokenStore.setPrefix("TOKEN:");
            return redisTokenStore;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    3. 资源服务器配置类 ResourceServerAutoConfiguration
    @Slf4j
    @Configuration
    @EnableResourceServer
    @EnableGlobalMethodSecurity(prePostEnabled = true)
    public class ResourceServerAutoConfiguration extends ResourceServerConfigurerAdapter {
       
        @Autowired
        private TokenStore tokenStore;
    
        @Value("${spring.application.name}")
        private String appName;
    
        @Override
        public void configure(ResourceServerSecurityConfigurer resources) {
            resources.resourceId(appName);
            resources.tokenStore(tokenStore);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    4. META-INF/spring.factories 文件中添加自动配置类
    # Auto Configure
    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
    com.hh.config.ResourceServerAutoConfiguration,\
    com.hh.config.TokenStoreAutoConfiguration
    
    • 1
    • 2
    • 3
    • 4

    2. 搭建认证服务器

    1. 安全配置类 WebSecurityConfig

    这里数据源是存储在内存中的,所以通过里配置的用户名和密码来获取token:

    /**
     * - @EnableGlobalMethodSecurity:该注解是用来开启权限注解
     * - prePostEnabled = true:开启Spring Security提供的四个权限注解,@PostAuthorize、@PostFilter、@PreAuthorize 以及@PreFilter
     */
    @Configuration
    @EnableGlobalMethodSecurity(prePostEnabled = true)
    @EnableWebSecurity
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Autowired
        private PasswordEncoder passwordEncoder;
    
        @Override
        @Bean
        public UserDetailsService userDetailsService() {
            // 内存数据库:用户名root,密码123
            InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();
            inMemoryUserDetailsManager.createUser(User
                    .withUsername("root")
                    .password(passwordEncoder.encode("123"))
                    .roles("ADMIN").build());
            return inMemoryUserDetailsManager;
        }
    
        //去除security的ROLE_前缀限制
        @Bean
        public RoleVoter roleVoter(){
            RoleVoter roleVoter = new RoleVoter();
            roleVoter.setRolePrefix("");
            return roleVoter;
        }
    
        /**
         * 放行和认证规则
         */
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            super.configure(http);
        }
    
        // 自定义AuthenticationManager:并没有在工厂中暴露出来
        // 使用AuthenticationManagerBuilder来自定义AuthenticationManager,覆盖默认的AuthenticationManager
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth.userDetailsService(userDetailsService());
        }
    
        // 开发者如需使用AuthenticationManager,
        // 则可以通过覆盖此方法,将configure(AuthenticationManagerBuilder)方法构造的AuthenticationManager暴露为Bean。
        @Bean
        @Override
        public AuthenticationManager authenticationManagerBean() throws Exception {
            return super.authenticationManagerBean();
        }
    }
    
    • 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
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    2. 授权服务器配置类 AuthorizationServerConfiguration
    /**
     * 配置授权服务器
     */
    @Configuration
    @EnableAuthorizationServer
    public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
    
        @Autowired
        private UserDetailsService userDetailsService;
    
        @Autowired
        private AuthenticationManager authenticationManager;
    
        @Autowired
        private PasswordEncoder passwordEncoder;
    
        @Autowired
        private TokenStore tokenStore;
    
        /**
         * 用来配置授权服务器可以为哪些客户端授权,使用哪种授权模式
         *
         * 密码模式:在密码模式中,用户向客户端提供用户名和密码,客户端通过用户名和密码到认证服务器获取令牌
         * (A)用户访问客户端,提供URI连接包含用户名和密码信息给授权服务器
         * (B)授权服务器对客户端进行身份验证
         * (C)授权通过,返回access_token给客户端
         */
        @Override
        public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
            clients.inMemory()
                    // 客户端id
                    .withClient("client_id")
                    // 客户端秘钥
                    .secret(passwordEncoder.encode("client_secret"))
                    // 授权模式
                    .authorizedGrantTypes("password", "refresh_token", "client_credentials")
                    // access_token的过期时长
                    .accessTokenValiditySeconds(43200)
                    // refresh_token的过期时长
                    .refreshTokenValiditySeconds(86400)
                    // 允许授权的范围
                    .scopes("all");
        }
    
        /**
         * 刷新令牌必须配置userDetailsService,用来刷新令牌时的认证
         */
        @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
            // 配置token的存储方式
            endpoints.tokenStore(tokenStore);
            // 配置认证管理器
            endpoints.authenticationManager(authenticationManager);
            // 配置认证数据源,刷新令牌时的认证
            endpoints.userDetailsService(userDetailsService);
        }
    
        /**
         * 开启token检查接口
         */
        @Override
        public void configure(AuthorizationServerSecurityConfigurer security){
            security.allowFormAuthenticationForClients().checkTokenAccess("permitAll()");
        }
    }
    
    • 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
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    3. Rest调用配置类 RestTemplateConfig
    @Configuration
    public class RestTemplateConfig {
        @Bean
        public RestTemplate restTemplate() {
            return new RestTemplate();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    4. 认证登录并获取access_token
    @Slf4j
    @RestController
    @RequestMapping("/api/v1")
    public class OAuthController {
    
        @Autowired
        private LoginService loginService;
    
        @PostMapping("/token")
        public ApiResponse<AuthToken> oauth(@RequestBody @Valid LoginQo loginQo) {
            AuthToken authToken = loginService.getAccessToken(loginQo);
            return new ApiResponse<>(0,"success",authToken);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    @Service
    @Slf4j
    public class LoginServiceImpl implements LoginService {
    
        @Autowired
        private RestTemplate restTemplate;
        
       /**
         * 通过登录信息获取令牌
         * @param loginQo 登录信息
         * @return 令牌
         */
        @Override
        public AuthToken getAccessToken(LoginQo loginQo) {
            MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
            map.add("username", loginQo.getName());
            map.add("password", loginQo.getPassword());
            map.add("client_id", "client_id");
            map.add("client_secret","client_secret");
            map.add("grant_type", "password");
            map.add("scope", "all");
            // 这里会进行 Oauth2.0 的请求:http://127.0.0.1:8081/oauth/token
            Map response = restTemplate.postForObject("http://127.0.0.1:8081/oauth/token", map, Map.class);
            if (MapUtil.isEmpty(response)) {
                return null;
            }
            // 封装返回
            AuthToken authToken = new AuthToken();
            authToken.setAccessToken((String) response.get("access_token"));
            authToken.setExpiresIn((Integer) response.get("expires_in"));
            authToken.setRefreshToken((String) response.get("refresh_token"));
            authToken.setTokenType((String) response.get("token_type"));
            authToken.setScope((String) response.get("scope"));
            return authToken;
        }
    }
    
    • 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
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    需要在资源服务器配置类中放行获取token的请求:

    @Slf4j
    @Configuration
    @EnableResourceServer
    @EnableGlobalMethodSecurity(prePostEnabled = true)
    public class ResourceServerAutoConfiguration extends ResourceServerConfigurerAdapter {
       
        @Autowired
        private TokenStore tokenStore;
    
        @Value("${spring.application.name}")
        private String appName;
    
        @Override
        public void configure(ResourceServerSecurityConfigurer resources) {
            resources.resourceId(appName);
            resources.tokenStore(tokenStore);
        }
        
        @Override
        public void configure(HttpSecurity http) throws Exception {
            http.authorizeRequests()
                // 放行获取token的请求
                .antMatchers("/api/v1/token").permitAll()
                // 其他请求必须认证才能访问
                .anyRequest().authenticated()
                .and()
                .csrf().disable();
        }
    }
    
    • 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
    5. 启动项目测试

    在这里插入图片描述

  • 相关阅读:
    Python数据容器(列表)
    Latex写作如何划钩与打叉
    数学建模、统计建模、计量建模整体框架的理解以及建模的步骤
    如何应对老板需求?
    Node.js版本管理工具nvm安装
    【21天学习挑战赛学习打卡】顺序查找
    mysql死锁查看
    智能语音热水器:置入NRK3301离线语音识别ic 迈向智能家居新时代
    盘点30个Python树莓派源码Python爱好者不容错过
    Node.js 20 —— 几个令人大开眼界的特性
  • 原文地址:https://blog.csdn.net/qq_42764468/article/details/127697728