Spring Security与CAS单点登录

目录

Spring Security介绍

Spring Security认证流程

Demo

自定义登录页与退出

从数据库中验证用户

密码加密

CAS单点登录系统

先说单点登录是何物?

再来说说CAS又是何物?

CAS服务端部署

CAS认证

cas客户端过滤器配置文件web.xml

CAS客户端与Spring Security集成


Spring Security介绍

Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反转Inversion of Control ,DI:Dependency Injection 依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。

Spring Security认证流程

  1. 用户使用用户名和密码进行登录。
  2. Spring Security 将获取到的用户名和密码封装成一个实现了 Authentication 接口的 UsernamePasswordAuthenticationToken。
  3. 将上述产生的 token 对象传递给 AuthenticationManager 进行登录认证。
  4. AuthenticationManager 认证成功后将会返回一个封装了用户权限等信息的 Authentication 对象。
  5. 通过调用 SecurityContextHolder.getContext().setAuthentication(...) 将 AuthenticationManager 返回的 Authentication 对象赋予给当前的 SecurityContext。

下面对上面提到的类进行简单的介绍
Authentication:认证用户信息的接口,用户登录认证之前之后有不同的实现类。认证成功之后保存在SecurityContextHolder持有的SecurityContext中。
SecurityContextHolder:使用ThreadLocal来保存SecurityContext。为了保证安全,在每一次request结束后都将清除当前线程ThreadLocal。
AuthenticationManager:处理认证(Authentication)请求的接口。只有一个authenticate()方法,接收一个代表认证请求的Authentication对象作为参数,认证成功则返回一个封装了当前用户权限等信息的Authentication对象进行返回。AuthenticationManager委托其配置的AuthenticationProvider列表处理认证请求,每一个AuthenticationProvider依次进行认证,只要有一个AuthenticationProvider 认证后的结果不为null,则表示该AuthenticationProvider已经认证成功,之后的AuthenticationProvider将不再继续认证。如果所有的AuthenticationProvider的认证结果都为null,则表示认证失败,将抛出一个ProviderNotFoundException。

Demo

自定义登录页与退出

添加依赖




	org.springframework.security
	spring-security-web
	{spring-security-version}


	org.springframework.security
	spring-security-config
	{spring-security-version}

XML配置方式

初始配置文件web.xml


	

  	 
		contextConfigLocation
		classpath:spring-security.xml
	 
	 
		
			org.springframework.web.context.ContextLoaderListener
		
	 
	
         
	   
		springSecurityFilterChain  
		org.springframework.web.filter.DelegatingFilterProxy  
	   
	   
		springSecurityFilterChain  
		/*  
	 
	

Spring Security配置文件spring-security.xml




	
	
	

	
	
		
		
		
		
                
		
                
                
	
	
	
	
		
			
				
			
			
	
		

注解方式

Spring配置类AppConfig.java,替代Spring的XML配置文件

@EnableWebMvc
@Configuration
@ComponentScan({"com.springsecurity.*"})
public class AppConfig {
    @Bean
    public SpringResourceTemplateResolver springResourceTemplateResolver() {
        SpringResourceTemplateResolver springResourceTemplateResolver = new SpringResourceTemplateResolver();
        springResourceTemplateResolver.setPrefix("/WEB-INF/pages/");
        springResourceTemplateResolver.setSuffix(".html");
        springResourceTemplateResolver.setTemplateMode("HTML");
        springResourceTemplateResolver.setCacheable(false);
        springResourceTemplateResolver.setCharacterEncoding("UTF-8");
        return springResourceTemplateResolver;
    }
    @Bean
    public SpringTemplateEngine springTemplateEngine() {
        SpringTemplateEngine springTemplateEngine = new SpringTemplateEngine();
        springTemplateEngine.setTemplateResolver(springResourceTemplateResolver());
        return springTemplateEngine;
    }
    @Bean
    public ThymeleafViewResolver thymeleafViewResolver() {
        ThymeleafViewResolver thymeleafViewResolver = new ThymeleafViewResolver();
        thymeleafViewResolver.setTemplateEngine(springTemplateEngine());
        thymeleafViewResolver.setCharacterEncoding("UTF-8");
        return thymeleafViewResolver;
    }
}

Spring Security配置类SecurityConfig.java,替代Spring Security的XML配置文件spring-security.xml

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

	@Autowired
	public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
	  auth.inMemoryAuthentication().withUser("admin").password("123456").roles("USER");
	}

	@Override
	protected void configure(HttpSecurity http) throws Exception {

	  http.authorizeRequests()
		.antMatchers("/**").access("hasRole('ROLE_USER')")
		.and().formLogin().loginPage("/login.html");
		
	}
}

扩展类SpringSecurityInitializer.java继承AbstractSecurityWebApplicationInitializer自动地加载SpringSecurityFilterChain,相当于在web.xml中配置spring security的filter

public class SpringSecurityInitializer extends AbstractSecurityWebApplicationInitializer {
   //do nothing
}

初始化加载类SpringMvcInitializer.java,替代web.xml文件

public class SpringMvcInitializer 
       extends AbstractAnnotationConfigDispatcherServletInitializer {

	@Override
	protected Class[] getRootConfigClasses() {
		return new Class[] { AppConfig.class };
	}

	@Override
	protected Class[] getServletConfigClasses() {
		return null;
	}

	@Override
	protected String[] getServletMappings() {
		return new String[] { "/" };
	}
	
}

从数据库中验证用户

再添加两个依赖


    org.springframework
    spring-jdbc
    ${spring.version}


      mysql
      mysql-connector-java
      ${mysql.connector.version}

数据库语句

CREATE TABLE users (
  username VARCHAR(20) NOT NULL ,
  password VARCHAR(20) NOT NULL ,
  enabled BOOLEAN DEFAULT TRUE NOT NULL,
  PRIMARY KEY (username)
);
CREATE TABLE user_roles (
  user_role_id int(11) NOT NULL AUTO_INCREMENT,
  username varchar(20) NOT NULL,
  role varchar(20) NOT NULL,
  PRIMARY KEY (user_role_id),
  UNIQUE KEY uni_username_role (role,username),
  KEY fk_username_idx (username),
  CONSTRAINT fk_username FOREIGN KEY (username) REFERENCES users (username)
);

XML方式

数据库配置文件spring-database.xml



    
        
        
        
        
    

修改spring-security.xml配置文件:



    
    
        
        
        
        
        
        
    
    
        
            
        
    

在web.xml中添加对数据库配置文件的指定


        contextConfigLocation
        
            /WEB-INF/spring-security.xml
            /WEB-INF/spring-database.xml
        

注解方式

把数据库配置文件改为在配置类AppConfig中配置,添加以下代码

@EnableWebMvc
@Configuration
@ComponentScan({"com.springsecurity.*"})
public class AppConfig {
    @Bean
    public SpringResourceTemplateResolver springResourceTemplateResolver() {
        SpringResourceTemplateResolver springResourceTemplateResolver = new SpringResourceTemplateResolver();
        springResourceTemplateResolver.setPrefix("/WEB-INF/pages/");
        springResourceTemplateResolver.setSuffix(".html");
        springResourceTemplateResolver.setTemplateMode("HTML");
        springResourceTemplateResolver.setCacheable(false);
        springResourceTemplateResolver.setCharacterEncoding("UTF-8");
        return springResourceTemplateResolver;
    }

    @Bean(name = "dataSource")
    public DriverManagerDataSource dataSource() {
        DriverManagerDataSource driverManagerDataSource = new DriverManagerDataSource();
        driverManagerDataSource.setDriverClassName("com.mysql.jdbc.Driver");
        driverManagerDataSource.setUrl("jdbc:mysql://127.0.0.1:3306/security");
        driverManagerDataSource.setUsername("root");
        driverManagerDataSource.setPassword("root");
        return driverManagerDataSource;
    }

    @Bean
    public SpringTemplateEngine springTemplateEngine() {
        SpringTemplateEngine springTemplateEngine = new SpringTemplateEngine();
        springTemplateEngine.setTemplateResolver(springResourceTemplateResolver());
        springTemplateEngine.addDialect(new SpringSecurityDialect());
        return springTemplateEngine;
    }

    @Bean
    public ThymeleafViewResolver thymeleafViewResolver() {
        ThymeleafViewResolver thymeleafViewResolver = new ThymeleafViewResolver();
        thymeleafViewResolver.setTemplateEngine(springTemplateEngine());
        thymeleafViewResolver.setCharacterEncoding("UTF-8");
        return thymeleafViewResolver;
    }
}

修改SecurityConfig.java

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

        @Resource
        private DataSource dataSource;

        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.jdbcAuthentication().dataSource(dataSource)
                .usersByUsernameQuery("select username,password, enabled from users where username = ?")
                .authoritiesByUsernameQuery("select username, role from user_roles where username = ?");
    }

	@Autowired
	public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
	  auth.inMemoryAuthentication().withUser("admin").password("123456").roles("USER");
	}

	@Override
	protected void configure(HttpSecurity http) throws Exception {

	  http.authorizeRequests()
		.antMatchers("/**").access("hasRole('ROLE_USER')")
		.and().formLogin().loginPage("/login.html");
		
	}
}

密码加密

XML方式

修改spring-security.xml配置文件,创建和注入PasswordEncoder到 AuthenticationProvider并设置作为身份验证提供者在AuthenticationManagerBuilder 



    
    
        
        
        
        
        
        
    
    
        
            
        
    
     
    

    

注解方式

修改SecurityConfig.java

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

        @Resource
        private DataSource dataSource;

        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.jdbcAuthentication().dataSource(dataSource)
                .usersByUsernameQuery("select username,password, enabled from users where username = ?")
                .authoritiesByUsernameQuery("select username, role from user_roles where username = ?");
    }

	@Autowired
	public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
	  auth.inMemoryAuthentication().withUser("admin").password("123456").roles("USER");
	}

        @Autowired
	@Qualifier("customUserDetailsService")
	UserDetailsService userDetailsService;
	
	
	@Autowired
	public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
		auth.userDetailsService(userDetailsService);
		auth.authenticationProvider(authenticationProvider());
	}
	
	
	@Bean
	public PasswordEncoder passwordEncoder() {
	    return new BCryptPasswordEncoder();
	}
	
	
	@Bean
	public DaoAuthenticationProvider authenticationProvider() {
	    DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
	    authenticationProvider.setUserDetailsService(userDetailsService);
	    authenticationProvider.setPasswordEncoder(passwordEncoder());
	    return authenticationProvider;
	}


	@Override
	protected void configure(HttpSecurity http) throws Exception {

	  http.authorizeRequests()
		.antMatchers("/**").access("hasRole('ROLE_USER')")
		.and().formLogin().loginPage("/login.html");
		
	}
}

逻辑层实现在保存新的口令到数据库中之前进行密码编码加密

@Service("userService")
@Transactional
public class UserServiceImpl implements UserService{

	@Autowired
	private UserDao dao;
	
	@Autowired
	private PasswordEncoder passwordEncoder;

	
	public void save(User user){
		user.setPassword(passwordEncoder.encode(user.getPassword()));
		dao.save(user);
	}
	
	public User findById(int id) {
		return dao.findById(id);
	}

	public User findBySso(String sso) {
		return dao.findBySSO(sso);
	}
	
}

CAS单点登录系统

先说单点登录是何物?

单点登录(Single Sign On),目前比较流行的企业业务整合解决方案之一。在多数大型系统中也能见到,如天猫、京东。作用就是用户在系统中登录一次就可以访问所有相互信任的应用系统。而这一点session是无法做到的。

再来说说CAS又是何物?

简单来说,cas就是单点登录的一种实现,由耶鲁大学发起的开源项目,旨在为 Web 应用系统提供一种可靠的单点登录方法。

CAS 包含两个部分: CAS Server 和 CAS Client。CAS Server负责对用户的认证工作;CAS Client负责处理对客户端受保护资源的访问请求,需要登录时,重定向到 CAS Server。

CAS服务端部署

1、从官网http://developer.jasig.org/cas/下载服务器压缩包并解压

2、将解压目录cas-server-4.0.0-release\cas-server-4.0.0\modules路径下的cas-server-webapp-4.0.0.war包拷贝到Tomcat服务器webapps的目录下,可重命名为更简单的名称,如:cas.war。

3、启动Tomcat访问http://localhost:8080/cas即可看到cas首页

CAS认证

CAS默认使用的是HTTPS协议,如果使用HTTPS协议需要SSL安全证书(需向特定的机构申请和购买),我们在本地机器可生成供本地使用的证书,如下:

生成证书的命令参数介绍

-genkeypair 生成密钥-keyalg 指定密钥算法

-keysize 指定密钥长度,默认是1024位

-siglag 指定数字签名算法

-validity 指定证书有效期,以天为单位

-alias 指定别名

-storepass 指定密码

-keystore 指定密钥库存储位置

-dname 指定用户信息

1、打开JDK安装目录\bin路径下的keytool.exe

keytool -genkeypair -alias cas -keyalg RSA -storepass changeit

在用户目录生成.keystore文件,删除的命令为

keytool -delete -alias <别名> -storepass <密码>

查看的命令为

keytool -list -storepass <密码>

2、导出证书

keytool -exportcert -alias <别名> -file <导出文件名,以crt结尾。如cas.crt> -storepass <密码>

3、导入证书到JVM

keytool -importcert -alias cas-file cas.crt -keystore "%JAVA_HOME%\jre\lib\security\cacerts" -storepass changeit –noprompt

4、修改Tomcat配置文件server.xml以支持https协议

(1)将

去掉注释修改为

  

(2)将


改为


5、启动Tomcat访问https://localhost:8443/cas

如果觉得以上安装证书太麻烦,开发测试阶段也可以直接去掉https安全认证,使用http协议

(1)修改cas的WEB-INF/deployerConfigContext.xml文件

    

改为

    

(2)修改cas的/WEB-INF/spring-configuration/ticketGrantingTicketCookieGenerator.xml文件


改为


(3)修改cas的WEB-INF/spring-configuration/warnCookieGenerator.xml文件,修改的东西与第(2)步一样

cas客户端过滤器配置文件web.xml


	
	
	  
      
      
        org.jasig.cas.client.session.SingleSignOutHttpSessionListener  
      
  
      
      
        CAS Single Sign Out Filter  
        org.jasig.cas.client.session.SingleSignOutFilter  
      
      
        CAS Single Sign Out Filter  
        /*  
      
  
      
      
        CASFilter  
        org.jasig.cas.client.authentication.AuthenticationFilter  
          
            casServerLoginUrl  
            http://localhost:8080/cas/login  
              
          
          
            serverName  
            http://localhost:8080
          
      
      
        CASFilter  
        /*  
      
  
      
      
        CAS Validation Filter  
          
            org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter  
          
            casServerUrlPrefix  
            http://localhost:8080/cas  
          
          
            serverName  
            http://localhost:8080
          
      
      
        CAS Validation Filter  
        /*  
      
  
      
      
        CAS HttpServletRequest Wrapper Filter  
          
            org.jasig.cas.client.util.HttpServletRequestWrapperFilter  
      
      
        CAS HttpServletRequest Wrapper Filter  
        /*  
      
  
      
      
        CAS Assertion Thread Local Filter  
        org.jasig.cas.client.util.AssertionThreadLocalFilter  
      
      
        CAS Assertion Thread Local Filter  
        /*  
      
  
      
	
	

CAS客户端与Spring Security集成

Spring Security通过spring-security-cas插件集成对CAS的封装,可以将上面CAS客户端一系列的配置以bean的形式嫁接到Spring Security的配置文件中,也就是Spring的依赖注入。

示例:

添加依赖

	

		
			org.springframework
			spring-core
			${spring.version}
		
		
			org.springframework
			spring-web
			${spring.version}
		

		
			org.springframework
			spring-webmvc
			${spring.version}
		

		
			org.springframework
			spring-context-support
			${spring.version}
		

		
			org.springframework
			spring-test
			${spring.version}
		

		
			org.springframework
			spring-jdbc
			${spring.version}
		
		
		
			javax.servlet
			servlet-api
			2.5
			provided
		

		
			org.springframework.security
			spring-security-web
			4.1.0.RELEASE
		

		
			org.springframework.security
			spring-security-config
			4.1.0.RELEASE
		

	
		
		
			org.springframework.security
			spring-security-cas
			4.1.0.RELEASE
		
		
			org.jasig.cas.client
			cas-client-core
			3.3.3
			
				
					org.slf4j
					log4j-over-slf4j
				
			
		

	

初始配置文件web.xml


	

  	 
		contextConfigLocation
		classpath:spring-security.xml
	 
	 
		
			org.springframework.web.context.ContextLoaderListener
		
	 
	
	   
		springSecurityFilterChain  
		org.springframework.web.filter.DelegatingFilterProxy  
	   
	   
		springSecurityFilterChain  
		/*  
	 
	
	
	
	springmvc
	org.springframework.web.servlet.DispatcherServlet
	
		
		contextConfigLocation
		classpath:springmvc.xml
		
	
	
		springmvc
		*.do
	
		

springmvc.xml



   
   
    
		

Spring Security配置文件spring-security.xml



	
	
	
	
	  
           
          
                   
              
          
          
    
    
  	
      
          
          
          
          
      
          
        
      
    

    
    
      
          
      
		
	
		
		
	
		
	  
          
              
                  
              
          
          
        
          
              
                  
              
          
         
            
   		 
	  
	
	
	
	
	     
        
          
      
          
          
              
          
          
      
      
	

认证类UserDetailServiceImpl.java,继承UserDetailsService,实现对用户角色权限的认证

public class UserDetailServiceImpl implements UserDetailsService {

	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		System.out.println("经过认证类:"+username);
		
		List authorities=new ArrayList();
		authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
		
		return new User(username,"",authorities);
	}

}

控制类UserController.java

@RestController
public class UserController {

	@RequestMapping("/findLoginUser")
	public void findLoginUser(){
		//当前登录名
		String name = SecurityContextHolder.getContext().getAuthentication().getName();
		System.out.println("当前登录名:"+name);
	}
	
}

index.html





首页


Hello,spring security01!  退出

index2.html





首页


Hello,spring security02!你已退出!

经过spring-security.xml文件的配置,从index.html退出后会跳转到index2.html页面,index2.html不在拦截列表。

你可能感兴趣的:(Spring Security与CAS单点登录)