Spring Security是如何保护Spring的
Spring Security是什么?
信息可能使我们最有用的价值,总有一些人会想办法来窃取我们的数据和身份信息。作为软件开发人员,我们必须去保护程序中的信息。而Sping Security正是Spring为我们提供的一个安全框架。其实Sping Security在Spring Boot没发布之前,就已经发展了很多年了,但是没有Shiro用的人多。因为在传统项目中,Shiro比较好整合,虽然没有Sping Security功能强大,但是也够用,够用嘛那就简单点好。
启用Sping Security
在pom文件中添加Sping Security依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>
这样就保护了我们的程序了,当应用启动的时候,强大自动配置会检测到Sping Security出现在类路径中,因此他会初始化一些安全配置。如下
- 所有的HTTP请求路径都需要认证
- 不需要特定的角色和权限
- 没有登录页面
- 认证过程是通过HTTP basic认证对话框实现的
- 系统只有一个用户,用户名为user
但是我们大多数应用的安全需求和这些基础的安全特性不一样,我们需要登录,注册,角色,鉴权……等等,那我们就需要编写一些显示的配置,覆盖掉自动配置为我们提供的功能。
以前都是冗长的XML配置等,近几年的几个版本,Sping Security都支持基于Java的配置,这种方式更加容易开发和阅读(毕竟写代码谁都会,我们要的是写出人能看懂的代码)。
我们可以通过覆盖WebSecurityConfigurerAdapter基础配置类中定义的configure()方法来进行配置。
package cn.stylefeng.guns; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import javax.sql.DataSource; @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired DataSource dataSource; @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth .jdbcAuthentication() .dataSource(dataSource).usersByUsernameQuery("select username,password from users where username = ?") .authoritiesByUsernameQuery("select username,authority from userAuthorities where username = ?"); } }
这么做了之后,用户的密码会以明文方式存储在数据库中,一般数据库中密码会进行转码处理,那这样的sql就会查询失败,因为密码与账号不匹配。
所以我们改造代码。
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.jdbcAuthentication()
.dataSource(dataSource).usersByUsernameQuery("select username,password from users where username = ?")
.authoritiesByUsernameQuery("select username,authority from userAuthorities where username = ?")
.passwordEncoder(new StandardPasswordEncoder("ds65ws"));
}
passwordEncoder对密码进行加密处理,这个方法也接受Sping Security中PasswordEncoder接口的任意实现。Sping Security的加密模块包括了多个这样的实现。
- BCryptPasswordEncoder:使用bcrypt强哈希加密
- NoOpPasswordEncoder:不进行任何转码
- Pbkdf2PasswordEncoder:使用PBKDF2加密
- SCryptPasswordEncoder:使用scrypt哈希加密
- StandardPasswordEncoder:使用SHA-256哈希加密
甚至你可以自定义的实现。PasswordEncoder接口很简单。
public interface PasswordEncoder{ String encode(CharSequence rawPassword); boolean matches(CharSequence rawPassword,String encodedPassword); }
不管用什么加密,数据库中的密码是永远不会解码的。
怎么做web防御
在SecurityConfig 类中添加如下方法:
@Override protected void configure(HttpSecurity httpSecurity) throws Exception { httpSecurity .authorizeRequests() //对Requests鉴权拦截 .antMatchers("/index","/order") //对/index、/order路径请求进行鉴权 .hasRole("admin") //需要admin角色才能访问 .antMatchers("/","/**") //放开其他页面 .permitAll(); //所有人都可以访问 }
一般我们是拦截所有页面,放开登录、注册、首页等不需要登录的页面出去。
对authorizeRequests的调用会返回一个对象,基于他我们可以对指定的url的安全需求。
声明在前面的规则会比后面的规则优先级高,例子中对/index和/order的拦截会高于放过所有请求。
保护路径的配置方法 | |
方法 | 做什么的 |
access(String) | 如果给定的SpEL表达式计算结果为true,就允许访问 |
anonymous() | 允许匿名访问 |
authenticated() | 允许认证过的用户访问 |
denyAll() | 无条件拒绝所有访问 |
fullAuthenticated() | 如果用户是完整认证的(不是通过Remember-me功能认证的),就允许访问 |
hasAnyAuthorith(String…) | 如果用户具备给定权限中的某一个,就允许访问 |
hasAnRole(String…) | 如果用户具备给定角色中的某一个,就允许访问 |
hasAuthority(String) | 如果用户具备给定去权限,就允许访问 |
hasIpAddress(String) | 如果请求来自给定IP地址,就允许访问 |
hasRole(String) | 如果用户具备给定角色,就允许访问 |
not() | 对其他访问方法的结果求反 |
permitAll() | 无条件允许访问 |
rememberMe() | 如果用户是通过Remember-me功能认证的,就允许访问 |
创建自定义登录页面
我们调用formLogin()配置自定义的登录表单。当Sping Security断定用户没有认证并且需要登录的时候,它就会将用户重定向到该路径。
@Override protected void configure(HttpSecurity httpSecurity) throws Exception { httpSecurity .authorizeRequests() .antMatchers("/index","/order") .hasRole("admin") .antMatchers("/","/**") .permitAll() .and() .formLogin() .loginPage("/login"); }
他还可以通过defaultSuccessUrl方法指定用户登录后调至的默认页面,以及是否强制调至默认页面。
@Override protected void configure(HttpSecurity httpSecurity) throws Exception { httpSecurity .authorizeRequests() .antMatchers("/index","/order") .hasRole("admin") .antMatchers("/","/**") .permitAll() .and() .formLogin() .loginPage("/login") // .defaultSuccessUrl("/index",true) //之前是不是登录页面都强制调至默认页面 .defaultSuccessUrl("/index"); //如果之前是登录页面,跳转默认页面 }
防止跨站请求伪造
CSRF是一种常见的安全攻击,Sping Security内置了CSRF保护。他默认是开启的,我们不需要配置他。我们需要做的就是每个表单中都要有一个名为“_csrf”的字段,他会持有CSRF token。
如果你用的Spring MVC的JSP标签库或者Sping Security的Thymeleaf方言,他会自动生成这个标签。
得到用户信息
在控制层中,我们只需要加入@AuthenticationPrincipal注解,我们就可以获取到用户信息。
@Controller
public class domeController {
@RequestMapping("/index")
public String index(String name, @AuthenticationPrincipal User user){
return null;
}
}
发表评论