好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

SpringSecurity OAtu2+JWT实现微服务版本的单点登录的示例

何为单点登录

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

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

认证中心

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

maven配置

?

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

   < 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 >

用户登录逻辑

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

@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测试数据maSeparatedStringToAuthorityList( "ROLE_NORMAL,ROLE_MEDIUM" ));

    }

}

OAtuh2配置

配置服务中心

?

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

@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

?

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

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());

    }

 

}

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

@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);

    }

}

请求模块

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

请求模块这个依赖很关键

?

1

2

3

4

5

6

7

8

        < 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也很重要

?

1

2

3

4

5

6

7

8

9

10

11

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是否合法的,这些都怎么用,是什么,后面会说

下面这个也很重要

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

@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();

    }

}

真实请求

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

?

1

2

3

4

5

6

7

    @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页,其他页也行.如果你从网关过来,然后你这里重定向回网关是不行的,除非你网关有相关操作,不然你网关只是一个转发功能,是先转发到对应模块,然后发现该模块的某个地址不可访问,才去认证中心请求权限的,所以这里的重定向地址还是具体到对应模块才对,其他不行,有多个用","隔开就行.

到此这篇关于SpringSecurity OAtu2+JWT实现微服务版本的单点登录的示例的文章就介绍到这了,更多相关SpringSecurity OAtu2 JWT单点登录内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!

原文链接:https://blog.csdn.net/weixin_47303191/article/details/124916250

查看更多关于SpringSecurity OAtu2+JWT实现微服务版本的单点登录的示例的详细内容...

  阅读:26次