天天看点

SpringBoot整合SpringSecurity(一)SpringBoot整合SpringSecurity(一)

SpringBoot整合SpringSecurity(一)

——手动配置用户登录校验信息

1.使用Spring Initialzr搭建一个简单地SpringBoot+SpringSecurity项目

1.1.创建一个新项目,选用模板为Spring Initializr:

SpringBoot整合SpringSecurity(一)SpringBoot整合SpringSecurity(一)

(因为本地网络原因,https://start.spring.io总是连接不上,所以我搭建了一个本地的Spring Initializr环境,此处用他来创建)

1.2.创建一个maven项目,模板选择web和security

SpringBoot整合SpringSecurity(一)SpringBoot整合SpringSecurity(一)
SpringBoot整合SpringSecurity(一)SpringBoot整合SpringSecurity(一)

此处可以选择Spring Boot的版本,建议使用Spring Boot 2.0以上版本。然后下一步,点击Finish,创建项目。

SpringBoot整合SpringSecurity(一)SpringBoot整合SpringSecurity(一)

然后在右下角点击选择Enable Auto Project 导入相应依赖,完成项目创建。

1.3.创建一个controller,测试一下

@RestController
public class HelloController {

    @GetMapping("/hello")
    public String hello(){
        return "hello security!";
    }
}
           

启动项目,访问localhost:8080/hello,页面会自己跳到一个登录界面

SpringBoot整合SpringSecurity(一)SpringBoot整合SpringSecurity(一)

默认登录名为:user

默认登录密码在控制台日志中打印出来了:Using generated security password: 360c3f49-6080-451c-889a-fd98ab527c6b

登陆成功后即可访问想要访问的页面。

SpringBoot整合SpringSecurity(一)SpringBoot整合SpringSecurity(一)

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:

SpringBoot整合SpringSecurity(一)SpringBoot整合SpringSecurity(一)

弹出熟悉的登录界面,使用user账号密码登录后尝试访问/admin/hello

SpringBoot整合SpringSecurity(一)SpringBoot整合SpringSecurity(一)

访问失败,原因是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

如有问题,敬请指正!

继续阅读