记录一下SpringSecurity的使用过程
写在前面
在学习项目的过程中,发现权限认证管理使用的是SpringSecurity,SpringSecurity功能非常强大,由于之前没接触过,所以此写下自己的入门使用过程。
什么是SpringSecurity?
Spring Security是一套认证授权框架,支持认证模式如HTTP BASIC 认证头 (基于 IETF RFC-based 标准),HTTP Digest 认证头 ( IETF RFC-based 标准),Form-based authentication (用于简单的用户界面),OpenID 认证等,Spring Security使得当前系统可以快速集成这些验证机制亦或是实现自己的一套验证机制.
如何在SpringBoot中使用SpringSecurity?
引入依赖
- 在创建项目的时候引入依赖。
- 直接在pom.xml中引入依赖。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
引入依赖后Spring Security默认保护所有接口。
配置Spring Security
Spring Security提供了一个适配器,可以在适配器的实现类中去配置Spring Security,包括认证方式、登录成功失败后跳转的页面、API接口的拦截与放行等。
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
}
@Override
public void configure(WebSecurity web) throws Exception {
}
@Override
protected void configure(HttpSecurity http) throws Exception {
}
}
WebSecurityConfigurerAdapter提供了三个configure函数需要我们去实现。
认证管理器配置方法
主要配置用户的认证、加密方式等。
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//自定义的用户和角色数据提供者
auth.userDetailsService(customUserDetailsService).passwordEncoder(new PasswordEncoder() { //设置密码加密对象
//encode():把参数按照特定的解析规则进行解析
@Override
public String encode(CharSequence charSequence) {
// 使用Spring自带的加密工具加密字段
return DigestUtils.md5DigestAsHex(charSequence.toString().getBytes());
}
// matches()验证从存储中获取的编码密码与编码后提交的原始密码是否匹配
// 第一个参数表示需要被解析的密码。第二个参数表示存储的密码。
@Override
public boolean matches(CharSequence charSequence, String s) {
return s.equals(DigestUtils.md5DigestAsHex(charSequence.toString().getBytes()));
}
});
}
由于项目中使用的用户名和密码是从数据库中获取的,所以我这里使用了一个自定义的用户和角色数据提供者customUserDetailsService。
public class CustomUserDetailsService implements UserDetailsService {
@Autowired
UserService userService;
@Autowired
RolesService rolesService;
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
User user= userService.loadUserByUsername(s);
if(user==null){
return new User();
}
List<Role> roles = rolesService.getRoleByUid(user.getId());
user.setRoles(roles);
return user;
}
}
自定义的用户和角色数据提供者需要实现UserDetailsService接口,这个接口很简单,只需要实现一个函数loadUserByUsername,这个函数需要我们自定义内容,实现通过用户名获取到一个UserDetails的实例对象。UserDetails也是一个接口,我用User对象去实现了UserDetails接口,所以我这里返回的是一个User对象。 下面是UserDetails的源码:
public interface UserDetails extends Serializable {
//返回用户的权限集, 默认需要添加ROLE_ 前缀
Collection<? extends GrantedAuthority> getAuthorities();
//获取用户密码
String getPassword();
//获取用户名
String getUsername();
//账户是否不过期
boolean isAccountNonExpired();
//账户是否不锁定
boolean isAccountNonLocked();
//账户凭证是否不过期
boolean isCredentialsNonExpired();
//账户是否启用
boolean isEnabled();
}
UserDetails接口需要我们实现7个函数。函数的作用我已经备注好了。
核心过滤器配置方法
一般用来设置忽略Spring Security 对静态资源的控制。
public void configure(WebSecurity web) throws Exception {
//忽略对静态资源的控制
web.ignoring().antMatchers("/blogimg/**","/index.html","favicon.ico","/static/**");
}
安全过滤器链配置方法。
这个方法比较重要也是使用最多的,HttpSecurity中需要我们配置很多东西,我们可以在HttpSecurity中自定义我们安全访问的规则。
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/admin/category/all")
.authenticated()
.antMatchers("/admin/**","/reg")
.hasRole("超级管理员")///admin/**和/reg的URL都需要有超级管理员角色,如果使用.hasAuthority()方法来配置,需要在参数中加上ROLE_,如下.hasAuthority("ROLE_超级管理员")
.anyRequest()
.authenticated()//其他的路径都是登录后即可访问
.and()
.formLogin()//指定支持基于表单的身份验证。如果未指定FormLoginConfigurer#loginPage(String),则将生成默认登录页面
.loginPage("/login_page")//设置登录页面
.failureUrl("/login_error")//设置登录失败后跳转的页面
.defaultSuccessUrl("/login_success",true)//设置登录成功后跳转的页面
.loginProcessingUrl("/login")//设置登录表单提交的页面
.usernameParameter("username")//设置表单提交用户名时使用的参数名
.passwordParameter("password")//设置表单提交密码时使用的参数名
.permitAll()//允许任何人访问
.and()
.logout()
.permitAll()
.and()
.csrf()
.disable()
.exceptionHandling()
.accessDeniedHandler((httpServletRequest, httpServletResponse, e) -> {
httpServletResponse.setStatus(HttpServletResponse.SC_FORBIDDEN);
httpServletResponse.setContentType("application/json;charset=utf-8");
PrintWriter printWriter = httpServletResponse.getWriter();
printWriter.write("权限不足,请联系管理员");//返回失败json数据
printWriter.flush();
printWriter.close();
});
}
最后
SpringSecurity的基础使用就配置完了,可以根据项目需求,选择返回的是一个页面还是返回Json数据。SpringSecurity功能比较强大,貌似配置也比较麻烦一点?等有空我学了另一个安全管理框架–Shiro再来做个对比。