Spring Security简介
Spring Security 是一个功能强大且高度可定制的身份验证和访问控制框架。它是保护基于 Spring 的应用程序的事实标准。
Spring Security 是一个专注于为 Java 应用程序提供身份验证和授权的框架。像所有 Spring 项目一样,Spring Security 的真正强大之处在于它可以轻松扩展以满足自定义需求
总结起来,SpringSecurity做的事情主要就是身份验证和授权,在一个网站中,想要完成这些事情,通过拦截器我们也可以实现,但是通过SpringSecurity我们可以更加优雅和方便的完成这项功能,同时它的高度可定制可以帮助我们完成一些自己的扩展需求。
依赖导入
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- 如果是和thymleaf整合则需要添加如下依赖 -->
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
</dependency>
功能整合
SpringSecurity和其他组件一样,配置只需要继承一个配置类并重写其中的方法即可。
首先我们来看下下面一个配置类。
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
// UserDetailsService:用于查询数据库名和数据库密码的过程
private UserDetailsService userDetailService;
//授权
@Override
protected void configure(HttpSecurity http) throws Exception {
// 首页所有人可以访问,功能页只有对应有权限的人才能访问
// 请求授权的规则
http.authorizeHttpRequests()
.antMatchers("/").permitAll()
.antMatchers("/admin/**").hasRole("admin")
.antMatchers("/user/**").hasAnyRole("admin", "user")
.antMatchers("/group_leader/**").hasRole("group_leader")
// 没有权限默认到登录页
.and().formLogin().loginPage("/toLogin").loginProcessingUrl("/login")
.successHandler((request, response, authentication) -> {
response.setContentType("application/json;charset=utf-8");
response.sendRedirect("/index");
})
.failureHandler((request, response, exception)->{
response.setContentType("application/json;charset=utf-8");
response.sendRedirect("/toLogin/fair");
})
.and().logout().logoutSuccessUrl("/")
// 开启记住我功能
.and().rememberMe().rememberMeParameter("remember")
// 注销 关闭csrf功能
.and().csrf().disable();
}
//认证
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailService).passwordEncoder(passwordEncoder());
}
@Bean
PasswordEncoder passwordEncoder() {
//返回一个具体的实现类
return new BCryptPasswordEncoder();
}
//认证
// @Override
// protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
// .withUser("jiangxja").password(new BCryptPasswordEncoder().encode("123456")).roles("user")
// .and()
// .withUser("admin").password(new BCryptPasswordEncoder().encode("admin")).roles("admin");
// }
}
我们需要做的只是继承WebSecurityConfigurerAdapter 类并实现其中的认证和授权方法即可。
由于采用的是链式编程,只需在http.authorizeHttpRequests()后面不断地加限制条件即可。
antMatchers用来匹配访问url地址对应的角色信息,hasRole和hasAnyRole用来配置角色。
注意: 这里的多个链是从上往下的顺序,即上面的优先级高于下面的。
formLogin用来配置login信息,因为SpringSecurity自带了登录界面,若想自定义页面只需配置loginPage,并把登录请求配置成loginProcessingUrl("/login")即可,同时,框架自带的请求参数为用户名username,密码password,因此若要自定义则需使用usernameParameter和passwordParameter来定义。
successHandler用来定义登录成功后的操作,同理,failureHandler用来定义失败的。
.logout().logoutSuccessUrl("/")用来定义登出操作,以及成功跳转请求地址url。
.rememberMe().rememberMeParameter(“remember”)用来定义记住我功能。
.csrf().disable()可以关闭csrf功能。
我们通过上述授权代码讲解知道了授权和角色是关联的,角色又需要通过登录验证,那么如何进行角色验证呢?
SpringSecurity提供了很多验证方法,基于内存的,基于JDBC的,基于数据库的。基于内存的为上面代码中注释部分,本人觉得用处不大,基于JDBC的也是将账号密码写在代码中,当初始化的时候加载进数据库,本人觉得一般也不会这样用,而一般是将权限和账号密码配置好,然后进行验证即可。
因此提供了auth.userDetailsService(userDetailService)用来自定义认证类。
下面是基于Mybatis Plus实现的权限认证功能。
@Service("userDetailsService")
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private UsersMapper usersMapper;
@Autowired
private AuthoritiesMapper authoritiesMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//调用userMapper里面的方法查询数据库,
QueryWrapper<Users> userWrapper = new QueryWrapper();
//根据用户名去数据库查询 where username = ?
userWrapper.eq("username", username).eq("enabled", true);
Users users = usersMapper.selectOne(userWrapper);
//判断
if (users == null) {
//数据库没有该用户名,认证失败
throw new UsernameNotFoundException("用户名不存在");
}
QueryWrapper<Authorities> authoritiesWrapper = new QueryWrapper();
authoritiesWrapper.eq("username", username);
List<Authorities> authorities = authoritiesMapper.selectList(authoritiesWrapper);
List<SimpleGrantedAuthority> role = new ArrayList<>();
for (Authorities authority : authorities) {
role.add(new SimpleGrantedAuthority("ROLE_" + authority.getAuthority()));
}
//从数据库返回的users对象中,得到用户名和密码,返回
return new User(users.getUsername(),
new BCryptPasswordEncoder().encode(users.getPassword()), role);
}
}
数据库表如下:
CREATE TABLE `authorities` (
`username` varchar(50) NOT NULL,
`authority` varchar(50) NOT NULL,
UNIQUE KEY `ix_auth_username` (`username`,`authority`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
CREATE TABLE `users` (
`username` varchar(50) NOT NULL,
`password` varchar(50) NOT NULL,
`enabled` tinyint(1) NOT NULL DEFAULT '1',
PRIMARY KEY (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
后端获取权限信息
认证成功后可从SecurityContextHolder.getContext().getAuthentication()获取认证信息。
authentication.getName()可以获取用户信息,无权限时为anonymousUser,有权限时为登录的username。
@RequestMapping({"index", ""})
public String toIndex(ModelMap model) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
String user = authentication.getName();
if (!ObjectUtil.equals("anonymousUser", user)) {
model.addAttribute("username", user);
QueryWrapper<UsersCoin> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("name", user);
UsersCoin userCoin = usersCoinService.getOne(queryWrapper);
if(Objects.isNull(userCoin)){
// 插入 Users_coin表
userCoin = new UsersCoin();
}
model.addAttribute("userCoin", userCoin);
}
return "index";
}
前端获取权限信息
当然如果配合thymleaf,只需加上
<html lang="en" xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5">
则可以直接在前端通过
<div sec:authorize="isAuthenticated()"><div>
判断是否有权限
<div sec:authorize="hasRole('user')||hasRole('admin')" ><div>
判断有什么权限,或者通过${session.SPRING_SECURITY_CONTEXT.authentication.principal.username}
拿到权限信息,且可以作为参数进行传递。
总结
SpringSecurity只是一个工具,我们需要灵活地运用它,需要根据具体场景不断的尝试,一篇小小的博客毕竟只能展示它的一部分用法,只有自己多踩踩坑,才能在实际运用中得心应手。
Spring Security比你想象的要简单!