任何一个网站在开始之前都应该要考虑到安全。主要都分为两个功能:验证和鉴权。验证就是确认用户的身份,一般采用用户名和密码的形式;鉴权就是确认用户拥有的身份(角色、权限)能否访问受保护的资源。
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;
}
}
测试页面自己随便写几个就可以
没有设置权限的时候可以正常访问,任意一个页面都能进去
授权
@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();
点击页面没有权限的时候可以自动跳转到登录页面去 查看源码,注释
/**
* 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用户登录,所有的页面都能访问
使用其他用户对应只能访问对应权限的用户
注销
点击注销,跳转到首页
@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
更换版本
让页面只显示该用户所能操作的版块
<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>