学习笔记:微服务-6.spring zuul + spring security + cas client 实现微服务sso登录

上节架构了spring zuul实现微服务的网页路由,因为zuul是微服务群的统一入口,非常适合在zuul服务上进行统一登录认证,本节实验结合spring zuul +spring security +Apereo cas实现微服务群的统一登录认证

spring security是一个spring的权限认证系统,cas是单位中央认证系统,从中央认证系统认证后,获取一个中央认证系统的身份(本测试中认证用户名linbin),spring security对这个用户名进行映射,比如这里映射为spring security的登录用户admin(可以通过数据库查询映射关系),并设置admin用户的权限,从而实现统一登录认证到本spring boot微服务的本地系统用户和权限的转换,而因zuul的统一api入口地位,这个登录可以作为整个微服务群的统一登录

1. 新建spring boot 微服务项目 Eureka-Client-zuul

pom.xml  全文


 
 
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation= "http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  4. <modelVersion>4.0.0 </modelVersion>
  5. <parent>
  6. <groupId>org.springframework.boot </groupId>
  7. <artifactId>spring-boot-starter-parent </artifactId>
  8. <version>2.1.1.RELEASE </version>
  9. <relativePath/> <!-- lookup parent from repository -->
  10. </parent>
  11. <groupId>com.linbin </groupId>
  12. <artifactId>Eureda-Client-zuul </artifactId>
  13. <version>0.0.1-SNAPSHOT </version>
  14. <name>Eureda-Client-zuul </name>
  15. <description>Demo project for Spring Boot </description>
  16. <properties>
  17. <project.build.sourceEncoding>UTF-8 </project.build.sourceEncoding>
  18. <project.reporting.outputEncoding>UTF-8 </project.reporting.outputEncoding>
  19. <java.version>1.8 </java.version>
  20. <spring-cloud.version>Greenwich.M3 </spring-cloud.version> <!-- Finchley.M8 -->
  21. </properties>
  22. <dependencies>
  23. <dependency>
  24. <groupId>org.springframework.boot </groupId>
  25. <artifactId>spring-boot-starter-web </artifactId>
  26. </dependency>
  27. <dependency>
  28. <groupId>org.springframework.cloud </groupId>
  29. <artifactId>spring-cloud-starter-netflix-eureka-client </artifactId>
  30. </dependency>
  31. <dependency>
  32. <groupId>org.springframework.cloud </groupId>
  33. <artifactId>spring-cloud-starter-netflix-zuul </artifactId>
  34. </dependency>
  35. <dependency>
  36. <groupId>org.springframework.cloud </groupId>
  37. <artifactId>spring-cloud-starter-config </artifactId>
  38. </dependency>
  39. <dependency>
  40. <groupId>org.springframework.boot </groupId>
  41. <artifactId>spring-boot-starter-security </artifactId>
  42. </dependency>
  43. <dependency>
  44. <groupId>org.springframework.security </groupId>
  45. <artifactId>spring-security-cas </artifactId>
  46. </dependency>
  47. <dependency>
  48. <groupId>org.springframework.boot </groupId>
  49. <artifactId>spring-boot-starter-test </artifactId>
  50. <scope>test </scope>
  51. </dependency>
  52. </dependencies>
  53. <dependencyManagement>
  54. <dependencies>
  55. <dependency>
  56. <groupId>org.springframework.cloud </groupId>
  57. <artifactId>spring-cloud-dependencies </artifactId>
  58. <version>${spring-cloud.version} </version>
  59. <type>pom </type>
  60. <scope>import </scope>
  61. </dependency>
  62. </dependencies>
  63. </dependencyManagement>
  64. <repositories>
  65. <repository>
  66. <id>spring-milestones </id>
  67. <name>Spring Milestones </name>
  68. <url>https://repo.spring.io/milestone </url>
  69. <snapshots>
  70. <enabled>false </enabled>
  71. </snapshots>
  72. </repository>
  73. </repositories>
  74. <build>
  75. <plugins>
  76. <plugin>
  77. <groupId>org.springframework.boot </groupId>
  78. <artifactId>spring-boot-maven-plugin </artifactId>
  79. </plugin>
  80. </plugins>
  81. </build>
  82. </project>

关键依赖:

      <dependency>
        <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-security</artifactId> 
         </dependency> 
   <dependency>
       <groupId>org.springframework.security</groupId>
       <artifactId>spring-security-cas</artifactId>
    </dependency>

2.  application.properties 配置

server.port=8000
eureka.client.serviceUrl.defaultZone=http://admin:123@centos7:8888/eureka/
spring.application.name=Euredaclientzuul
zuul.routes.euredaclient1.path=/c1/**
zuul.routes.euredaclient1.serviceId=Euredaclient1
zuul.routes.springdemo.path=/sd/**
zuul.routes.springdemo.serviceId=SpringDemo
spring.session.store-type=none

3. java文件目录

4. 启动类EuredaClientZuulApplication 

@EnableZuulProxy
@SpringBootApplication
public class EuredaClientZuulApplication {

    public static void main(String[] args) {
        SpringApplication.run(EuredaClientZuulApplication.class, args);
    }

}
5.WebSecurityConfig 类 是 spring security 配置实例


 
 
  1. @Configuration
  2. @EnableWebSecurity
  3. @EnableGlobalMethodSecurity(prePostEnabled = true, jsr250Enabled = true)
  4. public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
  5. @Autowired
  6. private AuthenticationEntryPoint authenticationEntryPoint;
  7. @Autowired
  8. private AuthenticationProvider authenticationProvider;
  9. @Autowired
  10. private SingleSignOutFilter singleSignOutFilter;
  11. @Autowired
  12. private LogoutFilter logoutFilter;
  13. @Override
  14. protected void configure(HttpSecurity http) throws Exception {
  15. //所有都需要认证才能访问 //由于设置了验证filter访问为,/login/cas,所以必须通过验证,否则出现死循环
  16. http
  17. .authorizeRequests()
  18. .antMatchers( "/login/cas")
  19. .permitAll()
  20. .and()
  21. .authorizeRequests()
  22. .anyRequest()
  23. .authenticated()
  24. .and()
  25. .httpBasic()
  26. .authenticationEntryPoint(authenticationEntryPoint)
  27. .and()
  28. .logout()
  29. .logoutSuccessUrl( "/logout")
  30. .and()
  31. .addFilterBefore(singleSignOutFilter, CasAuthenticationFilter.class)
  32. .addFilterBefore(logoutFilter, LogoutFilter.class);
  33. }
  34. @Override
  35. protected void configure(AuthenticationManagerBuilder auth) throws Exception {
  36. auth.authenticationProvider(authenticationProvider);
  37. }
  38. @Override
  39. protected AuthenticationManager authenticationManager() throws Exception {
  40. //设置cas认证提供
  41. return new ProviderManager( Arrays.asList(authenticationProvider));
  42. }
  43. @Bean
  44. public CasAuthenticationFilter casAuthenticationFilter(ServiceProperties sp) throws Exception {
  45. //cas认证过滤器,当触发本filter时,对ticket进行认证
  46. CasAuthenticationFilter filter = new CasAuthenticationFilter();
  47. filter.setServiceProperties(sp);
  48. filter.setAuthenticationManager(authenticationManager()); return filter; }
  49. @Override public void configure(WebSecurity web) throws Exception { super.configure(web);
  50. }
  51. }

6.  CasSecurityConfig 实现与cas的接口配置


 
 
  1. @Configuration
  2. public class CasSecurityConfig {
  3. //cas服务
  4. @Value( "${cas.server.url:https://author.linbsoft.com/cas}")
  5. private String casServerUrl;
  6. @Bean
  7. public ServiceProperties serviceProperties() {
  8. ServiceProperties serviceProperties = new ServiceProperties();
  9. //本机服务,访问/login/cas时进行校验登录
  10. serviceProperties.setService( "http://springcloud.linbsoft.com:8000/login/cas");
  11. serviceProperties.setSendRenew( false);
  12. return serviceProperties;
  13. }
  14. @Bean
  15. @Primary
  16. public AuthenticationEntryPoint authenticationEntryPoint( ServiceProperties sP) {
  17. CasAuthenticationEntryPoint entryPoint = new CasAuthenticationEntryPoint();
  18. //cas登录服务
  19. entryPoint.setLoginUrl(casServerUrl + "/login");
  20. entryPoint.setServiceProperties(sP);
  21. return entryPoint;
  22. }
  23. @Bean
  24. public TicketValidator ticketValidator() {
  25. //指定cas校验器
  26. return new Cas30ServiceTicketValidator( casServerUrl);
  27. }
  28. //cas认证
  29. @Bean
  30. public CasAuthenticationProvider casAuthenticationProvider() {
  31. CasAuthenticationProvider provider = new CasAuthenticationProvider();
  32. provider.setServiceProperties(serviceProperties());
  33. provider.setTicketValidator(ticketValidator());
  34. provider.setUserDetailsService(customUserDetailsService());
  35. provider.setKey( "CAS_PROVIDER_LOCALHOST_8000");
  36. return provider;
  37. }
  38. @Bean
  39. public UserDetailsService customUserDetailsService(){
  40. return new CustomUserDetailsService();
  41. }
  42. @Bean
  43. public SecurityContextLogoutHandler securityContextLogoutHandler() {
  44. return new SecurityContextLogoutHandler();
  45. }
  46. @Bean
  47. public LogoutFilter logoutFilter() {
  48. //退出后转发路径
  49. LogoutFilter logoutFilter = new LogoutFilter( casServerUrl + "/logout", securityContextLogoutHandler());
  50. //cas退出
  51. logoutFilter.setFilterProcessesUrl( "/logout/cas");
  52. return logoutFilter;
  53. }
  54. @Bean
  55. public SingleSignOutFilter singleSignOutFilter() {
  56. //单点退出
  57. SingleSignOutFilter singleSignOutFilter = new SingleSignOutFilter();
  58. singleSignOutFilter.setCasServerUrlPrefix(casServerUrl);
  59. singleSignOutFilter.setIgnoreInitConfiguration( true);
  60. return singleSignOutFilter;
  61. }
  62. //设置退出监听
  63. @EventListener
  64. public SingleSignOutHttpSessionListener singleSignOutHttpSessionListener( HttpSessionEvent event) {
  65. return new SingleSignOutHttpSessionListener();
  66. }
  67. }

7. CustomUserDetailsService 实现UserDetailsService接口

public class CustomUserDetailsService     implements UserDetailsService {
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        System.out.println("当前的用户名是:"+username);
        //这里为了方便测试,就直接返回一个用户信息,实际当中这里修改为查询数据库或者调用服务什么的来获取用户信息
        UserInfo userInfo = new UserInfo();
        userInfo.setId(104566569938L);
        userInfo.setUsername("admin");
        userInfo.setName("admin");
        Set<AuthorityInfo> authorities = new HashSet<AuthorityInfo>();
        AuthorityInfo authorityInfo = new AuthorityInfo("TEST-Roll");
        authorities.add(authorityInfo);
        authorityInfo = new AuthorityInfo("write-table-roll");
        authorities.add(authorityInfo);    
        userInfo.setAuthorities(authorities);
        return userInfo;
    }

}

8.   UserInfo  保存登录用户信息的类,CustomUserDetailsService 需要调用


 
 
  1. public class UserInfo implements UserDetails {
  2. private static final long serialVersionUID = - 1041327031937199938L;
  3. private Long id;
  4. private String name;
  5. private String username;
  6. private String password;
  7. private boolean isAccountNonExpired = true;
  8. private boolean isAccountNonLocked = true;
  9. private boolean isCredentialsNonExpired = true;
  10. private boolean isEnabled = true;
  11. private Set<AuthorityInfo> authorities = new HashSet<AuthorityInfo>();
  12. @Override
  13. public Collection<? extends GrantedAuthority> getAuthorities() {
  14. return authorities;
  15. }
  16. @Override
  17. public String getPassword() {
  18. return password;
  19. }
  20. @Override
  21. public String getUsername() {
  22. return username;
  23. }
  24. @Override
  25. public boolean isAccountNonExpired() {
  26. return this.isAccountNonExpired;
  27. }
  28. @Override
  29. public boolean isAccountNonLocked() {
  30. return this.isAccountNonLocked;
  31. }
  32. @Override
  33. public boolean isCredentialsNonExpired() {
  34. return this.isCredentialsNonExpired;
  35. }
  36. @Override
  37. public boolean isEnabled() {
  38. return this.isEnabled;
  39. }
  40. public void setUsername(String string) {
  41. this.username=string;
  42. }
  43. public void setName(String string) {
  44. this.name=string;
  45. }
  46. public String getName() {
  47. return this.name;
  48. }
  49. public void setAuthorities(Set<AuthorityInfo> authorities2) {
  50. authorities=authorities2;
  51. }
  52. public Long getId() {
  53. return id;
  54. }
  55. public void setId(Long id) {
  56. this.id = id;
  57. }
  58. public void setAccountNonExpired(boolean isAccountNonExpired) {
  59. this.isAccountNonExpired = isAccountNonExpired;
  60. }

9. AuthorityInfo 是保存权限信息的类,UserInfo类需要使用

public class AuthorityInfo  implements GrantedAuthority{
    private static final long serialVersionUID = -175781100474818800L;
    /**
     * 权限CODE
     */
    private String authority;
    public AuthorityInfo(String authority) {
        this.authority = authority;
    }
    @Override
    public String getAuthority() {
        return authority;
    }
    public void setAuthority(String authority) {
        this.authority = authority;
    }

}
 

10.  创建一个showuser类用来测试显示登录认证后的本地用户及权限

@RestController
public class showuser {
    @RequestMapping("/user")
    public String showloginuser() {
        UserDetails userDetails = (UserDetails) SecurityContextHolder.getContext() 
                .getAuthentication() 
                .getPrincipal(); 
        String user= "登陆用户:"+ userDetails.getUsername() +"<br>登陆权限:";
        for (GrantedAuthority grantedAuthority : userDetails.getAuthorities()) {
            user += ("<br>Authority:" + grantedAuthority.getAuthority());
        }
        return user;
    }
}

11. 全部类及配置介绍完毕,启动Eureda server微服务,再启动本例 Eureka-Client-zuul 服务

浏览器 http://springcloud.linbsoft.com:8000  本案例地址

会自动条转到 cas登录界面,登录后在eclipse控制台可以看见cas登录用户

控制台显示的是cas中央认证系统的登录用户

12. 在浏览器跳转到本地再进入 http://springcloud.linbsoft.com:8000/user 可以看见映射的本地账号

到此,实现了登录外部中央认证系统到登录本地应用的spring secutiry认证系统的流程

参考了多位网友的文章,在这一致致谢!

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值