随着程序的开发,安全性也越来越值得被注重,以前没有spring security这个框架的时候,我们要想实现权限页面拦截,都采用的拦截器(interceptor)或者过滤器(filter),特别的麻烦,在使用SpringSecurity后,一切都变的很简单,只需要几个简单的配置就能实现权限身份认证已经对数据进行加密等,非常的方便,无需自己去写复杂的逻辑来实现以前采用拦截器和过滤器才能实现的功能,SpringSecurity正是 带了Spring的前缀所以,它非常的出名,在Spring.io官网它被列为一个单独的项目。也在不停的更新,SpringSecurity也能被整合到任何项目中。
接下来我们就来讲解一下基本的操作:
如何使用SpringSecurity安全框架
一、首先在我们项目的pom.xml中引入SpringSecurity的核心依赖,
本教程使用的是SpringBoot最新版本2.1.4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
引入依赖成功以后,就会发现在启动的时候会出现一串随机密码
在项目中引入了SpringSecurity的依赖,默认会给你自动配置,默认的用户名是user,密码是每次启动控制台的一串字符串,每次启动这个密码都不一样。并且还会给你生成一个默认login登录页面
关于这个页面,这个页面是SpringSecurity官方提供的,但是没有人会使用这个页面来进行身份认证,都是自定义登录的页面来进行身份认证,但是处理登录逻辑是使用的这个接口
二、创建包开始写代码,项目结构如下
要想使用它,我们要完成 SpringSecurity最基本的配置,首先我们创建一个类WebSecurityConfg来继WebSecurityConfigurerAdapter这个适配器,关于这个适配器我不做深入的探讨,继承之后实现http的configure方法。
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter
注意加上注解
@Configuration
实现HttpSecurity的configure方法
只是默认实现的话,我们还不能使用,我们还需要进行简单的配置才能够让SpringSecurity发挥强大的作用。
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/").permitAll()
.anyRequest().authenticated()
.and()
.logout().permitAll()
.and()
.formLogin();
// 关闭默认认证
http.csrf().disable();
}
authorizeRequests定义那些url需要被保护,那些不需要进行保护,通常这个出来在配置的第一行,其中 antMatchers是设置路径,通常这里设置的是控制器(controller)上的请求路径,后面的permitAll是允许任何人访问,没有任何限制。
下面的anyRequest意思除了以上的请求,authenticated的意思是需要身份认证。
and()方法类似于xml配置中的结束标签,and()方法返回的对象还是HttpSecurity,当我们配置完成一段配置就要使用and来结束
formLogin是采用的是表单认证方式,还有一个httpBasic认证。
csrf.disable是 关闭跨站请求伪造保护
loginProcessingUrl是配置默认的登录入口(这里强调一遍Spring security默认的处理登录接口是/login这个自带的接口)
之后选择放行静态文件
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/js/**","/css/**","/images/**");
}
编写访问认证:
@RequestMapping("/")
private String index(){
return "success";
}
@RequestMapping("/hello")
private String hello(){
return "hello";
}
@PreAuthorize("hasAnyRole('ROLE_ADMIN')")
@RequestMapping("/role")
public String role(){
return "admin role";
}
这里我们选用的是自定义身份认证
//制定的身份
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("admin").password("123456").roles("ADMIN");
//明文提交
auth.inMemoryAuthentication().passwordEncoder(new MyPasswordEncoder()).withUser("xs").password("123456").roles("ADMIN");
//指定权限
auth.inMemoryAuthentication().withUser("lisi").password("123").roles("USER");
}
在指定类用户名和账号的时候,在启动的时候就不需要再次去使用默认的密码和账户进行登录,只需要我们自己设置的账号和密码登录。
有时候明文和指定的权限去登录会失败,那么解决的办法就是新建一个MyPasswordEncoder类去实现以下的方法
@Configuration
public class MyPasswordEncoder implements PasswordEncoder {
@Override
public String encode(CharSequence rawPassword) {
return rawPassword.toString();
}
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
return encodedPassword.equals(rawPassword.toString());
}
}
之后再明文提交或者指定权限提交去实例化
auth.inMemoryAuthentication().passwordEncoder(new MyPasswordEncoder()).withUser("xs").password("123456").roles("ADMIN");
三、接下来是数据库的认证。
首先先进行密码加密和匹配原则
public class MyPassword implements PasswordEncoder {
//指定盐值
private final static String SALT = "123456";
@Override
public String encode(CharSequence rawPassword) {
//加密
Md5PasswordEncoder encoder = new Md5PasswordEncoder();
return encoder.encodePassword(rawPassword.toString(),SALT);
}
//匹配
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
Md5PasswordEncoder encoder = new Md5PasswordEncoder();
return encoder.isPasswordValid(encodedPassword,rawPassword.toString(),SALT);
}
}
接着在指定的身份里面去实现
//制定的身份
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//自定义管理操作
auth.userDetailsService(myUserService).passwordEncoder(new MyPasswordEncoder());
//使用默认的数据库验证
auth.jdbcAuthentication().usersByUsernameQuery("").authoritiesByUsernameQuery("").passwordEncoder(new MyPasswordEncoder());
}
在进行数据库的自定义控制
@PreAuthorize("#id<10 and principal.username.equals(#username) and #user.username.equals('abc')")
@PostAuthorize("returnObject %2 ==0") //整数
@RequestMapping("/test")
public Integer test(Integer id , String username , User user){
//...自己的需求
return id;
}
//参数和返回值进行过滤
@PreFilter("filterObject%2 ==0") //偶数
@PostFilter("filterObject%4 ==0") //返回值
@RequestMapping("/test2")
public List<Integer> test2 (List<Integer> idList){
//..自己的需求
return idList;
}
这样设置了一样可以进行身份认证,但是如果security中配置了同一个url,那么security中配置要优先于使用@PreAuthorize配置的,这两种方式都能实现身份认证,看自己怎么选择。
@PreAuthorize的其它参数
hasRole (当前用户是否拥有指定角色)
hasAnyRole(多个角色是一个以逗号进行分隔的字符串。如果当前用户拥有指定角色中的任意一个则返回true)
hasAuthority(和hasRole一样的效果)
hasAnyAuthority(和hasAnyRole一样的效果)
hasPermission(不常用)里面有2个参数的,有三个参数的如下
@PreAuthorize(“hasPermission(‘user’, ‘ROLE_USER’)”)
@PreAuthorize(“hasPermission(‘targetId’,‘targetType’,‘permission’)”)