SpringBoot集成SpringSecurity项目搭建和登录测试(一)

SpringSecurity是基于Spring的一个功能强大且可高度自定义的身份认证和授权的访问控制框架。

以下基于SpringBoot2.4.4 +SpringSecurity5.4.5为例介绍。不同版本的集成有差异。

建议用SpringSecurity5.0以上的版本。

项目搭建

根据IDEA spring向导搭建SpringBoot和SpringSecurity的web项目。

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.cmdi</groupId>
    <artifactId>demo3</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo3</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

此时安全认证模式自动生效。

登录模式

编码测试用LoginController。

@Controller
public class LoginController {
​
    @RequestMapping(value = {"/","/index"})
    @ResponseBody
    public String index() {
        String a = "index";
        System.out.println(a);
        return a;
    }
}

HttpBasic表单提交登录

通过浏览器或postman访问http://localhost:8080/index,发现自动跳转到一个登录地址http://localhost:8080/login,要求输入用户名密码进行验证登录。

SpringSecurity默认访问控制模式采用HttpBasic表单提交方式。该方式拦截所有请求,默认的用户名是user,密码在控制台上中打印。

Using generated security password: e88b94cd-9d2e-47ac-a934-ba97eb683636

登录成功后在浏览器页面成功显示index。(为了方便做简单显示)

当然可以在springboot配置文件application.properties中配置默认的用户名密码:

spring.security.user.name=aaa
spring.security.user.password=123456

自定义formLogin登录

自定义登录方式指的是不使用SpringSecurity默认的登录页面,配置使用自写页面。

一、在resources/templates下创建mlogin.html文件为登录页面。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>用户登录</title>
</head>
<body>
<form action="/login" method="post">
    <p>
        <label>用户名</label>
        <input type="text" id="username" name="username">
    </p>
    <p>
        <label>密码</label>
        <input type="text" id="password" name="password">
    </p>
    <p>
        <input type="submit" value="登录">
    </p>
</form>
</body>
</html>

二、为了使得前端页面可用,在配置文件中声明如下。

spring.security.user.name=aaa
spring.security.user.password=123456

spring.resources.static-locations=classpath:/templates/
​

三、自定义配置类,继承org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter类,重写其中的三个configure方法。

WebSecurityConfigurerAdapter用来设定认证(登录)和授权(权限)。如果不做任何配置那么默认拦截所有的请求(包括登录),例如上面的/index被拦截需要登录。

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        super.configure(auth);
    }
    @Override
    public void configure(WebSecurity web) throws Exception {
        super.configure(web);
    }
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        super.configure(http);
    }
}
 

下面进行简单的配置,复写configure(HttpSecurity http):

http.authorizeRequests():表示拦截所有的请求,并进行处理。

antMatchers("/login","/mlogin.html").permitAll():对于登录页面和登录请求不拦截(满足条件的permitAll一律放行)。可以添加多个资源地址。其中/login为SpringSecurity默认的登录请求提交地址。

anyRequest().authenticated():其他的任何请求,全部拦截。

and():表示再返回一个HttpSecurity http方便链式设置。

formLogin().loginPage("/mlogin.html"):表示配置表单登录,并设置登录页面为/mlogin.html。

loginProcessingUrl("/login"):登录页面点提交后的请求地址(/login为SpringSecurity默认的登录请求提交地址)。

usernameParameter("username")、passwordParameter("password"):登录页面form表单中用户名、密码输入框对应的name值。(SpringSecurity会从request中获取输入信息)

defaultSuccessUrl("/index"):登录成功后默认的访问地址。

failureForwardUrl("/loginerror"):登录失败后默认的访问地址。

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        super.configure(auth);
    }
    @Override
    public void configure(WebSecurity web) throws Exception {
        super.configure(web);
    }
    @Override
    protected void configure(HttpSecurity http) throws Exception {
​
        //拦截并认证所有的请求
        http.authorizeRequests()
                //对于登录接口或登录页面不拦截
                .antMatchers("/login","/mlogin.html").permitAll()
                //所有的请求必须经过认证(包括登录),除非加入上面的不拦截
                .anyRequest().authenticated()
                //再返回一个HttpSecurity http
                .and()
                //设置登录页面
                .formLogin().loginPage("/mlogin.html")
                //登录页面填写完成后的提交地址,默认是/login(SpringSecurity已经默认实现了此接口)
                .loginProcessingUrl("/login")
                //登录页面form表单中用户名框对应的name
                .usernameParameter("username")
                //登录页面form表单中密码框对应的name
                .passwordParameter("password")
                //登录成功后访问
                .defaultSuccessUrl("/index")
                //登录失败后访问
                .failureForwardUrl("/loginerror");
        //关闭跨域攻击
        http.csrf().disable();
    }
}

用到的Controller如下。可以直接通过SpringContext上下文获取用户信息Authentication authentication = SecurityContextHolder.getContext().getAuthentication()。

@Controller
public class LoginController {
    @RequestMapping(value = {"/","/index"})
    @ResponseBody
    public String index() {
        String a = "index";
        System.out.println(a);
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        return a;
    }
    @RequestMapping("/loginerror")
    @ResponseBody
    public String loginerror() {
        String a = "login error";
        System.out.println(a);
        return a;
    }
}

测试结果分析:

  1. 访问http://localhost:8080/index,因为/index拦截,未通过认证,因此跳转到http://localhost:8080/mlogin.html【loginPage配置】。

  2. 输入错误的用户名密码,请求/loginerror【failureForwardUrl】,结果为login error。

  3. 输入正确的用户名密码aaa/123456,发现实际*问了/login接口【loginProcessingUrl】,认证成功后访问了/index接口【defaultSuccessUrl】,结果为/index。

静态权限设置

通过配置antMatchers.hasRole("")、hasAnyRole("","",...)、hasAuthority("")、hasAnyAuthority("","",...)来设定资源需要某角色或权限方可访问。

如下面代码中,设定/role的访问必须要用r1角色,设定/perm的访问必须要用p1权限。

//对于登录接口或登录页面不拦截
.antMatchers("/login","/mlogin.html").permitAll()
//对于接口访问必须需要某角色
.antMatchers("/role").hasRole("r1")
//对于接口访问必须需要某权限
.antMatchers("/perm").hasAuthority("p1")

测试接口如下。

    @RequestMapping("/role")
    @ResponseBody
    public String role() {
        String a = "role";
        System.out.println(a);
        return a;
    }
    @RequestMapping("/perm")
    @ResponseBody
    public String perm() {
        String a = "perm";
        System.out.println(a);
        return a;
    }

测试结果分析:

  1. 输入正确的用户名密码aaa/123456,结果为/index。

  2. 访问http://localhost:8080/permhttp://localhost:8080/role,此时显示type=Forbidden, status=403即无权限。因为尚未对用户aaa设定角色和权限,因此无法访问。

下一章,我们会将介绍加密方法和静态用户权限配置。

上一篇:「APT」- 获取构建 deb 包的编译选项(configure) @20210330


下一篇:第十六天104. 二叉树的最大深度