Spring Boot——搭建Spring Security(保姆级别)

??????

哈喽!大家好,我是【一心同学】,一位上进心十足的【Java领域博主】!???

✨【一心同学】的写作风格:喜欢用【通俗易懂】的文笔去讲解每一个知识点,而不喜欢用【高大上】的官方陈述。

✨【一心同学】博客的领域是【面向后端技术】的学习,未来会持续更新更多的【后端技术】以及【学习心得】。

✨如果有对【后端技术】感兴趣的【小可爱】,欢迎关注一心同学】???

❤️❤️❤️感谢各位大可爱小可爱!❤️❤️❤️ 


目录

1.前言

 (1)用户认证 (你是谁)

(2)用户授权 (你能干什么)

2.准备工作

3. 增加上认证和授权的功能

4.权限控制和注销

5.开启“记住我”功能

6.实现两个需求

7.自定义登录界面

8.完整配置类

结语


1.前言

在上一篇博客已经讲完了SpringSecurityOAuth的概念,了解到Spring Security是一个功能强大且高度可定制的身份验证和访问控制框架。

我们重新回顾下Spring Security的核心功能:

 (1)用户认证 (你是谁)

用户认证指的是验证某个用户是否为系统中的合法主体,也就是说用户能否访问该系统

(2)用户授权 (你能干什么)

用户授权指的是验证某个用户是否有权限执行某个操作

本篇博客将直接进行实操,我们来实现一个项目:

等级权限
VIP1访问level1目录下的静态资源
VIP2访问level2目录下的静态资源
VIP3访问level3目录下的静态资源

注:以下对SpringSecurity的搭建有参考【狂神】教学。

2.准备工作

1.新建一个初始的springboot项目web模块,thymeleaf模块

2.建立静态资源


3.编写Controller类

package com.yixin.demo.controller;


import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class RouterController {
    @RequestMapping({"/","/index"})
    public String index(){
        return "index";
    }

    @RequestMapping("/toLogin")
    public String toLogin(){
        return "views/login";
    }

    @RequestMapping("/level1/{id}")
    public String level1(@PathVariable("id") int id){
        return "views/level1/"+id;
    }

    @RequestMapping("/level2/{id}")
    public String level2(@PathVariable("id") int id){
        return "views/level2/"+id;
    }
    @RequestMapping("/level3/{id}")
    public String level3(@PathVariable("id") int id){
        return "views/level3/"+id;
    }
}

4.测试以上步骤是否成功

启动项目

首页:

登录页:

成功!

但我们目前是可以任意对level1~level3进行随意访问的,接着我们要做的就是只有相应权限的人才可以进行访问

3. 增加上认证和授权的功能

(1)导入Security依赖:

 <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>


(2)编写 Spring Security 配置类

重写WebSecurityConfigurerAdapter的两个配置方法:

configure(HttpSecurity http):定制请求的授权规则

configure(AuthenticationManagerBuilder auth):定义认证规则

package com.yixin.demo.config;

import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

@EnableWebSecurity// 开启WebSecurity模式
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
// 首页所有人都可以访问,功能也只有对应有权限的人才能访问到
// 请求授权的规则
        http.authorizeRequests()
                .antMatchers("/").permitAll()
                .antMatchers("/level1/**").hasRole("vip1")
                .antMatchers("/level2/**").hasRole("vip2")
                .antMatchers("/level3/**").hasRole("vip3");

// 开启自动配置的登录功能
// /login 请求来到登录页,如果没有指定默认登录界面,则会采用Security的默认登录界面
// /login?error 重定向到这里表示登录失败
        http.formLogin();
    }


    //定义认证规则
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//在内存中定义,也可以在jdbc中去拿....
//Spring security 5.0中新增了多种加密方式,也改变了密码的格式。
//要想我们的项目还能够正常登陆,需要修改一下configure中的代码。我们要将前端传过来的密码进行某种方式加密
//spring security 官方推荐的是使用bcrypt加密方式。

        auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
                .withUser("yixin").password(new BCryptPasswordEncoder().encode("123456")).roles("vip2","vip3")
                .and()
                .withUser("root").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1","vip2","vip3")
                .and()
                .withUser("guest").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1","vip2");
    }
}

(3)此时我们再重新尝试启动

我们点击Level2。

发现进入了SpringSecurity的默认登录界面,注意该界面并不是我们自定义的

我们输入以下信息:

user: yixin

pwd: 123456

我们对yixin这个用户给予的权限是vip2和vip3,也就是说yixin可以访问level2和level3,但是访问不了level1;

我们进行测试:

level2测试:

登录:

Level2目录下的页面可以成功进入。

测试level1目录:

点击:

可以发现我们无法访问level1目录下的静态资源,因为我们没有给yixin用户设置相应的权限

4.权限控制和注销

(1)主页面index.html增加一个注销的按钮

<a class="item" th:href="@{/logout}">
 <i class="address card icon"></i> 注销
</a>

(2)开启自动配置的注销的功能

//定制请求的授权规则
@Override
protected void configure(HttpSecurity http) throws Exception {
        //....
        //开启自动配置的注销的功能
        // /logout 注销请求
        http.logout();
        }

测试:

登录成功后点击注销

点击注销后会出来一个提示

点击log out,就会跳转到登录界面。

如果我们想让他注销成功后,依旧可以跳转到首页,应进行如下处理:

// .logoutSuccessUrl("/"); 注销成功来到首页
http.logout().logoutSuccessUrl("/");

测试成功!注销后跳转到首页。

5.开启“记住我”功能

现在的情况,我们只要登录之后,关闭浏览器,再进入网页,就会让我们重新登录,但是我们要实现的是当我们在该网站登录过一次,接着关闭网页,下一次进入该网站就不需要再进行登录了。

(1)SpringSecurity开启“记住我”配置:

protected void configure(HttpSecurity http) throws Exception {
        //......
        //开启记住我功能: cookie,默认保存两周,自定义接收前端的参数
        http.rememberMe();
        }

(2)启动项目测试

可以发现,这里多了一个Remember的标记

我们登录之后关闭 浏览器,然后重新打开浏览器访问,可以发现访问不需要登录了。

我们去查看一下浏览器的cookie,可以发现它的默认保存时间是14天

我们点击注销的时候,可以发现,spring security 帮我们自动删除了这个 cookie

(3)原理

cookie发送给浏览器保存,以后登录带上这个cookie,只要通过检查就可以免登录了。如果点击注销,则会删除这个cookie。

6.实现两个需求

(1用户没有登录的时候,导航栏上只显示登录按钮,用户登录之后,导航栏可以显示登录的用户信息及注销按钮。

(2)根据用户的权限,显示相应的页面,如yixin这个用户,它只有 vip2,vip3功能,那么登录则只显示这两个功能,而vip1的功能菜单不显示。

(1)导入依赖

   <dependency>
        <groupId>org.thymeleaf.extras</groupId>
        <artifactId>thymeleaf-extras-springsecurity5</artifactId>
        <version>3.0.4.RELEASE</version>
    </dependency>
  • 整合包4(springsecurity4)——springboot版本2.0.9
  • 整合包5(springsecurity5)——springboot版本之后

(2)thymeleaf的功能

sec:authorize="isAuthenticated()":是否认证登录!来显示不同的页面

(3)实现需求一

index.html

  <!--登录注销-->
            <div class="right menu">

                <!--如果未登录-->
                <div sec:authorize="!isAuthenticated()">
                    <a class="item" th:href="@{/toLogin}">
                        <i class="address card icon"></i> 登录
                    </a>
                </div>

                <!--如果已登录-->
                <div sec:authorize="isAuthenticated()">
                    <a class="item">
                        <i class="address card icon"></i>
                        用户名:<span sec:authentication="principal.username"></span>
                        角色:<span sec:authentication="principal.authorities"></span>
                    </a>
                </div>

                <div sec:authorize="isAuthenticated()">
                    <a class="item" th:href="@{/logout}">
                        <i class="address card icon"></i> 注销
                    </a>
                </div>
            </div>

启动项目

未登录:

登录过后:

(4)点击注销产生的问题

  • 整合包4(springsecurity4)

image-20200728220414292

  • 整合包5(springsecurity5)(不算问题,需要点击确定,才能回到首页)

    image-20200728220517534

解决问题:

  • 它默认防止csrf跨站请求伪造,因为会产生安全问题
  • 将请求改为post表单提交
  • 在spring security中关闭csrf功能
http.csrf().disable();

再次点击注销按钮之后(直接退出到首页

(5)实现需求二

<!--菜单根据用户的角色动态的实现-->
            <div class="column" sec:authorize="hasRole('vip1')">
                <div class="ui raised segment">
                    <div class="ui">
                        <div class="content">
                            <h5 class="content">Level 1</h5>
                            <hr>
                            <div><a th:href="@{/level1/1}"><i class="bullhorn icon"></i> Level-1-1</a></div>
                            <div><a th:href="@{/level1/2}"><i class="bullhorn icon"></i> Level-1-2</a></div>
                            <div><a th:href="@{/level1/3}"><i class="bullhorn icon"></i> Level-1-3</a></div>
                        </div>
                    </div>
                </div>
            </div>
            <div class="column" sec:authorize="hasRole('vip2')">
                <div class="ui raised segment">
                    <div class="ui">
                        <div class="content">
                            <h5 class="content">Level 2</h5>
                            <hr>
                            <div><a th:href="@{/level2/1}"><i class="bullhorn icon"></i> Level-2-1</a></div>
                            <div><a th:href="@{/level2/2}"><i class="bullhorn icon"></i> Level-2-2</a></div>
                            <div><a th:href="@{/level2/3}"><i class="bullhorn icon"></i> Level-2-3</a></div>
                        </div>
                    </div>
                </div>
            </div>
            <div class="column" sec:authorize="hasRole('vip3')">
                <div class="ui raised segment">
                    <div class="ui">
                        <div class="content">
                            <h5 class="content">Level 3</h5>
                            <hr>
                            <div><a th:href="@{/level3/1}"><i class="bullhorn icon"></i> Level-3-1</a></div>
                            <div><a th:href="@{/level3/2}"><i class="bullhorn icon"></i> Level-3-2</a></div>
                            <div><a th:href="@{/level3/3}"><i class="bullhorn icon"></i> Level-3-3</a></div>
                        </div>
                    </div>
                </div>

测试

未登录:

用户登录:

成功实现需求!

7.自定义登录界面

(1)主界面index中指定login请求

<div sec:authorize="!isAuthenticated()">
    <a class="item" th:href="@{/toLogin}">
        <i class="address card icon"></i> 登录
    </a>
</div>

(2)配置login.html登录的信息发送到哪里

注意:login.html 配置提交请求及方式,方式必须为post:

<form th:action="@{/login}" method="post">
    <div class="field">
        <label>Username</label>
        <div class="ui left icon input">
            <input type="text" placeholder="Username" name="username">
            <i class="user icon"></i>
        </div>
    </div>
    <div class="field">
        <label>Password</label>
        <div class="ui left icon input">
            <input type="password" name="password">
            <i class="lock icon"></i>
        </div>
    </div>
    <div class="field">
        <input type="checkbox" name="remember"> 记住我
    </div>
    <input type="submit" class="ui blue submit button"/>
</form>

(3)Spring Security对用户提交的登录信息进行验证处理

http.formLogin()
        .usernameParameter("username")//用户名
        .passwordParameter("password")//密码
        .loginPage("/toLogin")//进入登录界面的请求
        .loginProcessingUrl("/login"); // 登陆表单提交请求

我们再重新进行登录,就会弹出我们自定义的登录界面了。

8.完整配置类

package com.yixin.demo.config;

import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;


@EnableWebSecurity// 开启WebSecurity模式
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 定制请求的授权规则
        // 首页所有人可以访问
        http.authorizeRequests().antMatchers("/").permitAll()
                .antMatchers("/level1/**").hasRole("vip1")
                .antMatchers("/level2/**").hasRole("vip2")
                .antMatchers("/level3/**").hasRole("vip3");

        http.formLogin()
                .usernameParameter("username")
                .passwordParameter("password")
                .loginPage("/toLogin")
                .loginProcessingUrl("/login"); // 登陆表单提交请求

        http.csrf().disable();//关闭csrf功能:跨站请求伪造,默认只能通过post方式提交logout请求

        // /logout 注销请求
        http.logout().logoutSuccessUrl("/");

        //记住我
        http.rememberMe().rememberMeParameter("remember");
    }


    //定义认证规则
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //在内存中定义,也可以在jdbc中去拿....
        //Spring security 5.0中新增了多种加密方式,也改变了密码的格式。
        //要想我们的项目还能够正常登陆,需要修改一下configure中的代码。我们要将前端传过来的密码进行某种方式加密
        //spring security 官方推荐的是使用bcrypt加密方式。
        auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
                .withUser("yixin").password(new BCryptPasswordEncoder().encode("123456")).roles("vip2","vip3")
                .and()
                .withUser("root").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1","vip2","vip3")
                .and()
                .withUser("guest").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1","vip2");
    }
}


结语

以上就是一心同学通过观看B站大佬视频和网上的技术文章整理出来的基于SpringBoot对SpringSecurity的搭建,如果文章中有哪些地方讲得不清楚,欢迎提出来,让我们共同进步!


版权声明:本文为Huang_ZX_259原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。