SpringBoot整合SpringSecurity(一)
——手动配置用户登录校验信息
1.使用Spring Initialzr搭建一个简单地SpringBoot+SpringSecurity项目
1.1.创建一个新项目,选用模板为Spring Initializr:
(因为本地网络原因,https://start.spring.io总是连接不上,所以我搭建了一个本地的Spring Initializr环境,此处用他来创建)
1.2.创建一个maven项目,模板选择web和security
此处可以选择Spring Boot的版本,建议使用Spring Boot 2.0以上版本。然后下一步,点击Finish,创建项目。
然后在右下角点击选择Enable Auto Project 导入相应依赖,完成项目创建。
1.3.创建一个controller,测试一下
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello(){
return "hello security!";
}
}
启动项目,访问localhost:8080/hello,页面会自己跳到一个登录界面
默认登录名为:user
默认登录密码在控制台日志中打印出来了:Using generated security password: 360c3f49-6080-451c-889a-fd98ab527c6b
登陆成功后即可访问想要访问的页面。
2.手动配置登录用的用户名、密码、角色等信息
2.1.创建SecurityConfig配置类,继承WebSecurityConfigurerAdapter适配器类
我们在这里主要需要重写WebSecurityConfigurerAdapter中的三个configure()方法;
首先对这三个方法做一个大致的介绍:
//这个方法主要用来配置用户访问的一些认证策略,配置用户如何通过拦截器保护请求
protected void configure(HttpSecurity http) throws Exception {}
//这个方法主要用来配置登录用户的信息,配置user-detail服务
protected void configure(AuthenticationManagerBuilder auth) throws Exception {}
//这个方法主要用来配置Spring Security的Filter链
public void configure(WebSecurity web) throws Exception {}
这里放上我配置的代码:
@Configuration
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
//配置Bean,密码加密
@Bean
PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
//配置用户登录的信息
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("admin").password("$2a$10$Lspp.8iCCQqMwS/xB0mJvezv4JldRkt5ZM6g/Q1tubBOXXMZ1jMVG").roles("admin")
.and()
.withUser("user").password("$2a$10$Lspp.8iCCQqMwS/xB0mJvezv4JldRkt5ZM6g/Q1tubBOXXMZ1jMVG").roles("user");
}
//配置Spring Security的过滤器忽略静态资源
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/js/**","/css/**","/images/**");
}
//配置用户访问的认证策略
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
//配置角色匹配可以访问的路径
.antMatchers("/admin/**").hasRole("admin")
.antMatchers("/user/**").hasAnyRole("admin","user")
.anyRequest().authenticated()
.and()
//表单登录相关配置
.formLogin()
.loginProcessingUrl("/doLogin")
//配置登陆成功返回信息
.successHandler(new AuthenticationSuccessHandler() {
@Override
public void onAuthenticationSuccess(HttpServletRequest req, HttpServletResponse resp, Authentication auth) throws IOException, ServletException {
resp.setContentType("application/json;charset=utf-8");
PrintWriter out = resp.getWriter();
Map<String,Object> map = new HashMap<>();
map.put("status",200);
map.put("msg",auth.getPrincipal());
out.write(new ObjectMapper().writeValueAsString(map));
out.flush();
out.close();
}
})
//配置登录失败返回信息
.failureHandler(new AuthenticationFailureHandler() {
@Override
public void onAuthenticationFailure(HttpServletRequest req, HttpServletResponse resp, AuthenticationException e) throws IOException, ServletException {
resp.setContentType("application/json;charset=utf-8");
PrintWriter out = resp.getWriter();
Map<String,Object> map = new HashMap<>();
map.put("status",401);
if(e instanceof LockedException){
map.put("msg","账户被锁定,登陆失败");
}else if(e instanceof BadCredentialsException){
map.put("msg","用户名或者密码错误,登陆失败");
}else if(e instanceof DisabledException){
map.put("msg","账户被禁用,登陆失败");
}else if(e instanceof AccountExpiredException){
map.put("msg","账户过期,登陆失败");
}else{
map.put("msg","登陆失败");
}
out.write(new ObjectMapper().writeValueAsString(map));
out.flush();
out.close();
}
})
.permitAll()
.and()
//注销登录相关配置
.logout()
.logoutUrl("/logout")
//注销成功返回信息
.logoutSuccessHandler(new LogoutSuccessHandler() {
@Override
public void onLogoutSuccess(HttpServletRequest req, HttpServletResponse resp, Authentication auth) throws IOException, ServletException {
resp.setContentType("application/json;charset=utf-8");
PrintWriter out = resp.getWriter();
Map<String,Object> map = new HashMap<>();
map.put("status",200);
map.put("msg","注销成功");
out.write(new ObjectMapper().writeValueAsString(map));
out.flush();
out.close();
}
})
.and()
//关闭csrf,允许跨站请求伪造,此处禁用用于postman测试接口
.csrf().disable();
}
}
下面对 AuthenticationManagerBuilder 、HttpSecurity 以及 WebSecurity中的常用方法做一个解读(只参考到HttpSecurity的常用方法,其他两个后续再补上):
HttpSecurity : HttpSecurity
使用了builder
的构建方式来灵活制定访问策略。最早基于 XML
标签对 HttpSecurity
进行配置。现在大部分使用 javaConfig
方式。常用的方法解读如下:
方法 | 说明 |
---|---|
openidLogin() | 用于基于 OpenId 的验证 |
headers() | 将安全标头添加到响应,比如说简单的 XSS 保护 |
cors() | 配置跨域资源共享( CORS ) |
sessionManagement() | 允许配置会话管理 |
portMapper() | 允许配置一个PortMapper(HttpSecurity#(getSharedObject(class))),其他提供SecurityConfigurer的对象使用 PortMapper 从 HTTP 重定向到 HTTPS 或者从 HTTPS 重定向到 HTTP。默认情况下,Spring Security使用一个PortMapperImpl映射 HTTP 端口8080到 HTTPS 端口8443,HTTP 端口80到 HTTPS 端口443 |
jee() | 配置基于容器的预认证。 在这种情况下,认证由Servlet容器管理 |
x509() | 配置基于x509的认证 |
rememberMe | 允许配置“记住我”的验证 |
authorizeRequests() | 允许基于使用HttpServletRequest限制访问 |
requestCache() | 允许配置请求缓存 |
exceptionHandling() | 允许配置错误处理 |
securityContext() | 在HttpServletRequests之间的SecurityContextHolder上设置SecurityContext的管理。 当使用WebSecurityConfigurerAdapter时,这将自动应用 |
servletApi() | 将HttpServletRequest方法与在其上找到的值集成到SecurityContext中。 当使用WebSecurityConfigurerAdapter时,这将自动应用 |
csrf() | 添加 CSRF 支持,使用WebSecurityConfigurerAdapter时,默认启用 |
logout() | 添加退出登录支持。当使用WebSecurityConfigurerAdapter时,这将自动应用。默认情况是,访问URL”/ logout”,使HTTP Session无效来清除用户,清除已配置的任何#rememberMe()身份验证,清除SecurityContextHolder,然后重定向到”/login?success” |
anonymous() | 允许配置匿名用户的表示方法。 当与WebSecurityConfigurerAdapter结合使用时,这将自动应用。 默认情况下,匿名用户将使用org.springframework.security.authentication.AnonymousAuthenticationToken表示,并包含角色 “ROLE_ANONYMOUS” |
formLogin() | 指定支持基于表单的身份验证。如果未指定FormLoginConfigurer#loginPage(String),则将生成默认登录页面 |
oauth2Login() | 根据外部OAuth 2.0或OpenID Connect 1.0提供程序配置身份验证 |
requiresChannel() | 配置通道安全。为了使该配置有用,必须提供至少一个到所需信道的映射 |
httpBasic() | 配置 Http Basic 验证 |
addFilterBefore() | 在指定的Filter类之前添加过滤器 |
addFilterAt() | 在指定的Filter类的位置添加过滤器 |
addFilterAfter() | 在指定的Filter类的之后添加过滤器 |
and() | 连接以上策略的连接器,用来组合安全策略。实际上就是"而且"的意思 |
2.2.用户信息也可以在application.properties中配置
spring.security.user.name=admin
spring.security.user.password=123
spring.security.user.roles=admin
在application.properties中配置好用户信息后,就不用再在 configure(AuthenticationManagerBuilder auth) 中配置用户信息了,但好像只能配单用户,不建议这样配置
2.3.在Controller中添加测试方法,启动服务,测试
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello() {
return "hello security";
}
@GetMapping("/admin/hello")
public String admin() {
return "hello admin";
}
@GetMapping("/user/hello")
public String user() {
return "hello user";
}
}
在浏览器访问localhost:8080/hello:
弹出熟悉的登录界面,使用user账号密码登录后尝试访问/admin/hello
访问失败,原因是user账户没有admin角色权限,无法访问/admin/**,
同理登录admin账号,可以访问/user/hello,因为访问/user/**只需要有admin或者user任意一个角色就可以
其他路径下的不做角色限制,只要登录成功就能访问。
参考博客:
https://blog.csdn.net/z1790424577/article/details/80738507
https://blog.csdn.net/weixin_42849689/article/details/89957823
https://segmentfault.com/a/1190000020705382
本博客大量参考江南一点雨,松哥的Spring Boot+Vue全栈开发实战及对应视频教程,附上松哥博客链接:
https://www.javaboy.org
如有问题,敬请指正!