任何一个网站在开始之前都应该要考虑到安全。主要都分为两个功能:验证和鉴权。验证就是确认用户的身份,一般采用用户名和密码的形式;鉴权就是确认用户拥有的身份(角色、权限)能否访问受保护的资源。

SpringSecurity是针对Spring项目的安全框架,也是Spring Boot底层安全模块默认的技术,可以实现强大的web安全控制。只需要引入spring-boot-starter-security模块,进行少量的配置就可实现强大的安全管理

两个主要目标 认证和授权

搭好框架

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        super.configure(http);
    }
}

测试页面如下

控制层代码

package com.example.springboot1.controller;

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

/**
 * @author pangxie
 * @data 2020/10/24 20:53
 */
@Controller
public class RouterController {

    @RequestMapping({"/","/index"})
    public String login(){
        return "index";
    }

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

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

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

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

测试页面自己随便写几个就可以

springboot cloud nacos 鉴权 springsecurity鉴权_登录页面

没有设置权限的时候可以正常访问,任意一个页面都能进去

授权

@EnableWebSecurity
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");
    }
}

再次访问页面

springboot cloud nacos 鉴权 springsecurity鉴权_ide_02

没有对应的访问权限,进不去

 

可以设置没有权限默认去到登录页面

//没有权限默认去到login页面
http.formLogin();

点击页面没有权限的时候可以自动跳转到登录页面去 查看源码,注释

/**
     * Specifies to support form based authentication. If
     * {@link FormLoginConfigurer#loginPage(String)} is not specified a default login page
     * will be generated.
     *
     * <h2>Example Configurations</h2>
     *
     * The most basic configuration defaults to automatically generating a login page at
     * the URL "/login", redirecting to "/login?error" for authentication failure. The
     * details of the login page can be found on
     * {@link FormLoginConfigurer#loginPage(String)}
     *
     * <pre>
     * @Configuration
     * @EnableWebSecurity
     * public class FormLoginSecurityConfig extends WebSecurityConfigurerAdapter {
     *
     *  @Override
     *  protected void configure(HttpSecurity http) throws Exception {
     *      http.authorizeRequests().antMatchers("/**").hasRole("USER").and().formLogin();
     *  }
     *
     *  @Override
     *  protected void configure(AuthenticationManagerBuilder auth) throws Exception {
     *      auth.inMemoryAuthentication().withUser("user").password("password").roles("USER");
     *  }
     * }
     * </pre>
     *
     * The configuration below demonstrates customizing the defaults.
     *
     * <pre>
     * @Configuration
     * @EnableWebSecurity
     * public class FormLoginSecurityConfig extends WebSecurityConfigurerAdapter {
     *
     *  @Override
     *  protected void configure(HttpSecurity http) throws Exception {
     *      http.authorizeRequests().antMatchers("/**").hasRole("USER").and().formLogin()
     *              .usernameParameter("username") // default is username
     *              .passwordParameter("password") // default is password
     *              .loginPage("/authentication/login") // default is /login with an HTTP get
     *              .failureUrl("/authentication/login?failed") // default is /login?error
     *              .loginProcessingUrl("/authentication/login/process"); // default is /login
     *                                                                      // with an HTTP
     *                                                                      // post
     *  }
     *
     *  @Override
     *  protected void configure(AuthenticationManagerBuilder auth) throws Exception {
     *      auth.inMemoryAuthentication().withUser("user").password("password").roles("USER");
     *  }
     * }
     * </pre>
     *
     * @see FormLoginConfigurer#loginPage(String)
     *
     * @return the {@link FormLoginConfigurer} for further customizations
     * @throws Exception
     */
    public FormLoginConfigurer<HttpSecurity> formLogin() throws Exception {
        return getOrApply(new FormLoginConfigurer<>());
    }

认证

授权完毕过后我们就可以相应的认证,从以上这段源码的注释中可以看出认证

protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth.inMemoryAuthentication().withUser("user").password("password").roles("USER");
        }

这里先使用在内存中测试,正常应该连接数据库进行认证

//认证
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.inMemoryAuthentication().withUser("root").password("12345").roles("vip1","vip2","vip3")
                                 .and().withUser("xiaoxie").password("12345").roles("vip1","vip2")
                                 .and().withUser("xiaoxiang").password("12345").roles("vip1");
}

写好认证后启动访问跳转到登录出现以下错误 说密码没有加密

Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.

Sun Oct 25 10:54:44 CST 2020
There was an unexpected error (type=Internal Server Error, status=500).
There is no PasswordEncoder mapped for the id "null"

使用Spring Security自带的加密

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
   auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
                                .withUser("root").password(new BCryptPasswordEncoder().encode("12345")).roles("vip1","vip2","vip3")
                                .and().withUser("xiaoxie").password(new BCryptPasswordEncoder().encode("12345")).roles("vip1","vip2")
                                .and().withUser("xiaoxiang").password(new BCryptPasswordEncoder().encode("12345")).roles("vip1");
}

现在点击登录 使用root用户登录,所有的页面都能访问

springboot cloud nacos 鉴权 springsecurity鉴权_登录页面_03

springboot cloud nacos 鉴权 springsecurity鉴权_登录页面_04

springboot cloud nacos 鉴权 springsecurity鉴权_登录页面_05

使用其他用户对应只能访问对应权限的用户

注销

点击注销,跳转到首页

@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页面
        http.formLogin();

        //注销用户
        http.logout().logoutSuccessUrl("/");
    }

在页面显示出登录用户,和角色。登录后显示注销按钮,未登录显示登录按钮。一下却没有显示,原因是版本问题,版本调到2.1.0以下就可以。比如2.0.9

springboot cloud nacos 鉴权 springsecurity鉴权_ide_06

更换版本

springboot cloud nacos 鉴权 springsecurity鉴权_spring_07

让页面只显示该用户所能操作的版块

springboot cloud nacos 鉴权 springsecurity鉴权_登录页面_08

springboot cloud nacos 鉴权 springsecurity鉴权_ide_09

<div class="column" sec:authorize="hasRole('vip1')">
    <h3>Level1</h3>
    <a href="/level1/1">1</a>
    <a href="/level1/2">2</a>
    <a href="/level1/3">3</a>
    <hr>
</div>

<div class="column" sec:authorize="hasRole('vip2')">
    <h3>Level2</h3>
    <a href="/level2/1">1</a>
    <a href="/level2/2">2</a>
    <a href="/level2/3">3</a>
    <hr>
</div>

<div class="column" sec:authorize="hasRole('vip3')">
    <h3>Level3</h3>
    <a href="/level3/1">1</a>
    <a href="/level3/2">2</a>
    <a href="/level3/3">3</a>
    <hr>
</div>

配置类 SecurityConfig.java 全部代码

package com.example.springboot1.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;

/**
 * @author pangxie
 * @data 2020/10/24 21:15
 */
@EnableWebSecurity
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页面
        http.formLogin();

        //关闭csrf
        http.csrf().disable();
        //注销用户
        http.logout().logoutSuccessUrl("/");
    }

    //认证
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
                                     .withUser("root").password(new BCryptPasswordEncoder().encode("12345")).roles("vip1","vip2","vip3")
                                     .and().withUser("xiaoxie").password(new BCryptPasswordEncoder().encode("12345")).roles("vip1","vip2")
                                     .and().withUser("xiaoxiang").password(new BCryptPasswordEncoder().encode("12345")).roles("vip1");
    }
}

index.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
<head>
    <title>Thymeleaf demo</title>
</head>
<body>
<h1>Spring Security</h1>
<!--登录注册-->
<div>
    <!--如果未登录-->
    <div sec:authorize="!isAuthenticated()">
        <a class="item" th:href="@{/login}">
            登录
        </a>
    </div>

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

    <div sec:authorize="isAuthenticated()">
        <!--注销-->
        <a class="item" th:href="@{/logout}">
            注销
        </a>
    </div>

</div>
<div class="column" sec:authorize="hasRole('vip1')">
    <h3>Level1</h3>
    <a href="/level1/1">1</a>
    <a href="/level1/2">2</a>
    <a href="/level1/3">3</a>
    <hr>
</div>

<div class="column" sec:authorize="hasRole('vip2')">
    <h3>Level2</h3>
    <a href="/level2/1">1</a>
    <a href="/level2/2">2</a>
    <a href="/level2/3">3</a>
    <hr>
</div>

<div class="column" sec:authorize="hasRole('vip3')">
    <h3>Level3</h3>
    <a href="/level3/1">1</a>
    <a href="/level3/2">2</a>
    <a href="/level3/3">3</a>
    <hr>
</div>

</body>
</html>

pom.xml 文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.9.RELEASE</version>
        <relativePath/>
    </parent>
    <groupId>com.example</groupId>
    <artifactId>springboot1</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>Demo</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-web</artifactId>
        </dependency>

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

        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.2</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <!-- Thymeleaf 模板引擎依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

        <!--thymeleaf 和 Spring Security 整合包-->
        <dependency>
            <groupId>org.thymeleaf.extras</groupId>
            <artifactId>thymeleaf-extras-springsecurity4</artifactId>
            <version>3.0.4.RELEASE</version>
        </dependency>


        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>RELEASE</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>RELEASE</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>org.mybatis.generator</groupId>
                <artifactId>mybatis-generator-maven-plugin</artifactId>
                <version>1.3.5</version>
                <dependencies>
                    <dependency>
                        <groupId> mysql</groupId>
                        <artifactId>mysql-connector-java</artifactId>
                        <version>8.0.11</version>
                    </dependency>
                    <dependency>
                        <groupId>org.mybatis.generator</groupId>
                        <artifactId>mybatis-generator-core</artifactId>
                        <version>1.3.5</version>
                    </dependency>
                </dependencies>
                <executions>
                    <execution>
                        <id>Generate MyBatis Artifacts</id>
                        <phase>package</phase>
                        <goals>
                            <goal>generate</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <verbose>true</verbose>
                    <!-- 是否覆盖 -->
                    <overwrite>true</overwrite>
                    <!-- MybatisGenerator的配置文件位置 -->
                    <configurationFile>src/main/resources/mybatisGeneratorConfig.xml</configurationFile>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>