目录
Spring Security介绍
Spring Security认证流程
Demo
自定义登录页与退出
从数据库中验证用户
密码加密
CAS单点登录系统
先说单点登录是何物?
再来说说CAS又是何物?
CAS服务端部署
CAS认证
cas客户端过滤器配置文件web.xml
CAS客户端与Spring Security集成
Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反转Inversion of Control ,DI:Dependency Injection 依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。
下面对上面提到的类进行简单的介绍
Authentication:认证用户信息的接口,用户登录认证之前之后有不同的实现类。认证成功之后保存在SecurityContextHolder持有的SecurityContext中。
SecurityContextHolder:使用ThreadLocal来保存SecurityContext。为了保证安全,在每一次request结束后都将清除当前线程ThreadLocal。
AuthenticationManager:处理认证(Authentication)请求的接口。只有一个authenticate()方法,接收一个代表认证请求的Authentication对象作为参数,认证成功则返回一个封装了当前用户权限等信息的Authentication对象进行返回。AuthenticationManager委托其配置的AuthenticationProvider列表处理认证请求,每一个AuthenticationProvider依次进行认证,只要有一个AuthenticationProvider 认证后的结果不为null,则表示该AuthenticationProvider已经认证成功,之后的AuthenticationProvider将不再继续认证。如果所有的AuthenticationProvider的认证结果都为null,则表示认证失败,将抛出一个ProviderNotFoundException。
添加依赖
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);
}
}
单点登录(Single Sign On),目前比较流行的企业业务整合解决方案之一。在多数大型系统中也能见到,如天猫、京东。作用就是用户在系统中登录一次就可以访问所有相互信任的应用系统。而这一点session是无法做到的。
简单来说,cas就是单点登录的一种实现,由耶鲁大学发起的开源项目,旨在为 Web 应用系统提供一种可靠的单点登录方法。
CAS 包含两个部分: CAS Server 和 CAS Client。CAS Server负责对用户的认证工作;CAS Client负责处理对客户端受保护资源的访问请求,需要登录时,重定向到 CAS Server。
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默认使用的是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)步一样
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
/*
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不在拦截列表。