目录
  • 何为单点登录
  • 认证中心
    • maven配置
    • 用户登录逻辑
    • OAtuh2配置
      • 配置服务中心
      • 配置规则中心
  • 请求模块
    • 真实请求
      • 一些小问题

        何为单点登录

        单点登录通俗的话来讲在微服务当中,在一个服务登录后就能免去另一个服务的登录操作,所谓单点登录.

        就好像你在微博总网站里登录后,然后在微博里面的某一个模块点进去后,就发现这个模块竟然不用登录了,不是因为这个模块与主网站是一体的用一个SpringSecurity就可以搞定了,这里面的水深着呢!
        感兴趣更深这个SpringSecurity建议去看看图灵课堂的SpringSecurity,建议不要看尚硅谷的,那个版本说实话感觉有点老式了,教你手写,其实我觉得,你既然学到了这个层次了,就应该能建立起快速打通一个新技术的能力,这个"打通"的意思不是要你深入,而是会用!别想着深入,因为没有什么实际意义,现在不是我们小白应该做的事,我们小白只要打好基础就可以了,比如java,jvm,spring,mysql这些!
        没有SpringSecurity基础就别看这篇文章了,你可能看得懂,但是你肯定实现不出来,不信我你就看

        认证中心

        新建一个微服务模块,可以另外建项目,也可以直接在你微服务项目里建立模块就可以了.

        maven配置

          <dependencies>
                <dependency>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-thymeleaf</artifactId>
                </dependency>
                <dependency>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-web</artifactId>
                </dependency>
                <dependency>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-starter-oauth2</artifactId>
                </dependency>
                <dependency>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-starter-security</artifactId>
                </dependency>
        
                <dependency>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-test</artifactId>
                    <scope>test</scope>
                    <exclusions>
                        <exclusion>
                            <groupId>org.junit.vintage</groupId>
                            <artifactId>junit-vintage-engine</artifactId>
                        </exclusion>
                    </exclusions>
                </dependency>
            </dependencies>

        用户登录逻辑

        @Component
        public class SheepUserDetailsService implements UserDetailsService {
        
            @Autowired
            private PasswordEncoder passwordEncoder;
        
            @Override
            public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        
                if( !"admin".equals(s) )
                    throw new UsernameNotFoundException("用户" + s + "不存在" );
        
                return new User( s, passwordEncoder.encode("123456"), AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_NORMAL,ROLE_MEDIUM"));
            }
        }

        OAtuh2配置

        配置服务中心

        @Configuration
        @EnableAuthorizationServer
        public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
        
            @Autowired
            AuthenticationManager authenticationManager;
        
            @Autowired
            SheepUserDetailsService sheepUserDetailsService;
        
            @Override
            public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        
                // 定义了两个客户端应用的通行证
                clients.inMemory()
                        .withClient("admin")
                        .secret(new BCryptPasswordEncoder().encode("123456"))
                        .authorizedGrantTypes("authorization_code", "refresh_token","password")
                        .scopes("all")
                        .autoApprove(true)
                        .redirectUris("http://192.168.216.1:8001/login","http://192.168.216.1:8004/login");
        
            }
        
            @Override
            public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        
                endpoints.tokenStore(jwtTokenStore()).accessTokenConverter(jwtAccessTokenConverter());
                DefaultTokenServices tokenServices = (DefaultTokenServices) endpoints.getDefaultAuthorizationServerTokenServices();
                tokenServices.setTokenStore(endpoints.getTokenStore());
                tokenServices.setSupportRefreshToken(true);
                tokenServices.setClientDetailsService(endpoints.getClientDetailsService());
                tokenServices.setTokenEnhancer(endpoints.getTokenEnhancer());
                tokenServices.setAccessTokenValiditySeconds((int) TimeUnit.DAYS.toSeconds(1)); // 一天有效期
                endpoints.tokenServices(tokenServices);
        
                //密码模式配置
                endpoints.authenticationManager(authenticationManager).userDetailsService(sheepUserDetailsService);
            }
        
            @Override
            public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
                security
                        .tokenKeyAccess("isAuthenticated()")
                        .checkTokenAccess("permitAll()")
                        .allowFormAuthenticationForClients();
            }
        
            @Bean
            public TokenStore jwtTokenStore() {
                return new JwtTokenStore(jwtAccessTokenConverter());
            }
        
            @Bean
            public JwtAccessTokenConverter jwtAccessTokenConverter(){
                JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
                converter.setSigningKey("testKey");
                return converter;
            }
        
        }

        配置规则中心

        Configuration

        public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
        
        
            @Override
            @Bean
            public AuthenticationManager authenticationManager() throws Exception {
                return super.authenticationManager();
            }
        
            @Autowired
            private UserDetailsService userDetailsService;
        
            @Bean
            public PasswordEncoder passwordEncoder() {
                return new BCryptPasswordEncoder();
            }
        
        
            @Autowired
            private CustomLogoutSuccessHandler customLogoutSuccessHandler;
        
        
            @Bean
            public DaoAuthenticationProvider authenticationProvider() {
                DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
                authenticationProvider.setUserDetailsService(userDetailsService);
                authenticationProvider.setPasswordEncoder(passwordEncoder());
                authenticationProvider.setHideUserNotFoundExceptions(false);
                return authenticationProvider;
            }
        
            @Override
            protected void configure(HttpSecurity http) throws Exception {
                http.requestMatchers().antMatchers("/oauth/**","/login/**","/logout/**","/uac/oauth/token","/remove")
                        .and()
                        .authorizeRequests()
                        .antMatchers("/oauth/**").authenticated()
                        .and()
                        .formLogin().permitAll()
                        .and()
                        .logout()
                        .logoutSuccessHandler(customLogoutSuccessHandler)
                        // 无效会话
                        .invalidateHttpSession(true)
                        // 清除身份验证
                        .clearAuthentication(true)
                        .permitAll();
            }
        
        
            @Override
            protected void configure(AuthenticationManagerBuilder auth) throws Exception {
                auth.authenticationProvider(authenticationProvider());
            }
        
        }
        @Component
        public class CustomLogoutSuccessHandler extends AbstractAuthenticationTargetUrlRequestHandler implements LogoutSuccessHandler {
            @Override
            public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
                // 将子系统的cookie删掉
                //建议将token也删除,直接写个controller接口就可以了,可以在前端调用/logout的同时调用删除token接口
                Cookie[] cookies = request.getCookies();
                if(cookies != null && cookies.length>0){
                    for (Cookie cookie : cookies){
                        cookie.setMaxAge(0);
                        cookie.setPath("/");
                        response.addCookie(cookie);
                    }
                }
                super.handle(request, response, authentication);
            }
        }

        请求模块

        下面这个是请求模块,也就是独立出一个微服务,假如这个微服务是做业务的,会给认证中心发出请求,然后去热证,这个模块也算登录了.
        那么有道友会问:其他模块在该模块登录后还要登录吗?答案:不用!
        至于要以什么为基础,接着往下看:

        请求模块这个依赖很关键

                <dependency>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-starter-oauth2</artifactId>
                </dependency>
                <dependency>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-starter-security</artifactId>
                </dependency>

        这个yml也很重要

        auth-server: http://localhost:8085/uac
        security:
          oauth2:
            client:
              client-id: admin
              client-secret: 123456
              user-authorization-uri: ${auth-server}/oauth/authorize #认证
              access-token-uri: ${auth-server}/oauth/token #获取token
            resource:
              jwt:
                key-uri: ${auth-server}/oauth/token_key #忘了,反正要写上
        

        上面的认证和获取token这两个配置很重要,还有一个/oauth/check_token,这个是用来检查token是否合法的,这些都怎么用,是什么,后面会说

        下面这个也很重要

        @Configuration
        @EnableWebSecurity
        @EnableGlobalMethodSecurity(prePostEnabled = true)
        @EnableOAuth2Sso
        public class ClientWebsecurityConfigurer extends WebSecurityConfigurerAdapter {
        
            @Override
            public void configure(HttpSecurity http) throws Exception {
            //下面表示该模块所有请求都要拦截
            //因为在yml配置了认证中心的各个路径,所以会自动跳转到认证中心去认证
            //如果你想在当前模块排除哪些拦截,可以在下面去排除
                http.antMatcher("/**").authorizeRequests()
                        .anyRequest().authenticated();
            }
        }

        真实请求

        下面就可以正式去请求了,这里很多网友都会有疑问,就是前面我在认证中心授予了权限之后,在其他模块该如何去权限的规定呢?其实很简单,有很多博主都没有说,直接加上注解就可以了.

            @GetMapping("/get")
            @PreAuthorize("hasAuthority('ROLE_NORMAL')")
            public String get(HttpServletRequest request){
                System.out.println("函数进来了");
                return "uusb1j";
            }
        }

        这个时候,如果该微服务的get请求过来,就会跳转到认证中心去认证.

        一些小问题

        这个时候只要有相同配置的模块都不用自行登录了.如果有一个模块进行登出请求,所有服务都会进行登出.

        注意认证中心有项配置redirectUris(“http://192.168.216.1:8001/login”,“http://192.168.216.1:8004/login”),这个配置代表认证成功后重定向去哪个地址,但并不会真的重定向去对应模块的login页,而是重定向去你请求前被拦截的地址,但是这个有讲究就是必须配置请求前所在模块的地址,每个模块对应一个重定向地址,最好是重定向到对应模块的/login页,其他页也行.如果你从网关过来,然后你这里重定向回网关是不行的,除非你网关有相关操作,不然你网关只是一个转发功能,是先转发到对应模块,然后发现该模块的某个地址不可访问,才去认证中心请求权限的,所以这里的重定向地址还是具体到对应模块才对,其他不行,有多个用","隔开就行.

        声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。