本文最后更新于:February 1, 2022 pm
Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反转、依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。
目录 环境准备 1.页面 准备5个HTML页面,一个index首页,一个login登陆页面。然后div1目录下一个页面;div2目录下的一个页面;div3目录下的一个页面。
div1/1.html
<!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > Title</title > </head > <body > <h3 > div1------------- 1</h3 > </body > </html >
div2/1.html
<!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > Title</title > </head > <body > <h3 > div2------------- 1</h3 > </body > </html >
div3/1.html
<!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > Title</title > </head > <body > <h3 > div3------------- 1</h3 > </body > </html >
login.html
<!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > Title</title > </head > <body > <form action ="" > 用户名:<input type ="text" > <br > 密 码:<input type ="password" > <br > <input type ="submit" value ="登陆" > </form > </body > </html >
index.html
<!DOCTYPE html > <html lang ="en" xmlns:th ="http://www.thymeleaf.org" > <head > <meta charset ="UTF-8" > <title > Title</title > </head > <body > <hr > <a th:href ="@{/div1/1}" > 1-1</a > <br > <a th:href ="@{/div2/1}" > 2-1</a > <br > <a th:href ="@{/div3/1}" > 3-1</a > <br > </body > </html >
注意:我们在后面中的登录中,用的都是系统自带的登录,这里这个并没有用。
2.依赖 功能使用到的依赖
<dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-thymeleaf</artifactId > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-web</artifactId > </dependency >
实现 页面跳转控制 页面控制RouterController.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 package com.tothefor.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("/div1/{pid}") public String div1 (@PathVariable("pid") int id) { return "views/div1/" +id; } @RequestMapping("/div2/{pid}") public String div2 (@PathVariable("pid") int id) { return "views/div2/" +id; } @RequestMapping("/div3/{pid}") public String div3 (@PathVariable("pid") int id) { return "views/div3/" +id; } }
其中,@RequestMapping(“/div1/{pid}”) 后面的{pid} 表示传入一个参数,然后@PathVariable(“pid”)int id 表示把传入的参数给id。这样可以实现一个目录下的不同页面的配置,就不用每一个页面配置一个。
在把页面控制写完之后可以跑一下,看是否成功。
配置类 首先需要再加入一个依赖:
<dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-security</artifactId > </dependency >
再写一个类继承WebSecurityConfigurerAdapter,再用注解@EnableWebSecurity表示被Spring接管。并重写一个方法。
package com.tothefor.config;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;@EnableWebSecurity public class SecutityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure (HttpSecurity http) throws Exception { super .configure(http); } }
授权 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package com.tothefor.config;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;@EnableWebSecurity public class SecutityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure (HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/" ).permitAll() .antMatchers("/div1/**" ).hasRole("div1" ) .antMatchers("/div2/**" ).hasRole("div2" ) .antMatchers("/div3/**" ).hasRole("div3" ); } }
接下来,再一次跑项目,可以发现:首页可以访问,但div下面的访问不了。访问不了的,会自动到报错页面。我们可以自定义跳转到报错页面。添加
没有权限时,会跳到登录页面(/login),但这个url我们并没有设置,而且这个页面不是自己写的,而是自带的。具体可查看源码。而且我们后面进行登录都是在这个自带的页面进行登录。
认证 有内存认证(inMemoryAuthentication)和数据库认证(jdbcAuthentication)。用法相同,这里用内存认证为例。在授权类中再重写另外一个方法。
@Override protected void configure (AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication() .withUser("loong" ).password("123456" ).roles("div1" ) .and() .withUser("root" ).password("123456" ).roles("div1" ,"div2" ,"div3" ); }
正常情况这里都是从数据库中读取数据的。
这样配置好之后跑项目再访问时,会报一个错误,说的是密码并没有进行加密。还需要进行密码加密处理:
@Override protected void configure (AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder()) .withUser("loong" ) .password(new BCryptPasswordEncoder().encode("123456" )).roles("div1" ) .and() .withUser("root" ) .password(new BCryptPasswordEncoder().encode("123456" )).roles("div1" ,"div2" ,"div3" ); }
这下再跑项目就可以实现登录后访问。
连接数据库 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 package com.tothefor.config;import com.alibaba.druid.pool.DruidDataSource;import org.springframework.beans.factory.annotation.Autowired;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;import javax.sql.DataSource;@EnableWebSecurity public class SecutityConfig extends WebSecurityConfigurerAdapter { @Autowired private DataSource dataSource; private DruidDataSource ds = (DruidDataSource) dataSource; @Override protected void configure (AuthenticationManagerBuilder auth) throws Exception { auth.jdbcAuthentication().dataSource(ds) .withDefaultSchema() .withUser("loong" ).password("123" ).roles("" ) .and() .withUser("root" ).password("123" ).roles("" ); } }
注销 在授权方法中加入:
这样就可以实现注销功能。然后前端
<a th:href ="@{/logout}" > 注销</a >
注意:这里的/logout 不是我们自己配置的,而是自带的。
还能实现在成功注销后跳转到的页面:
http.logout().logoutSuccessUrl("/" );
权限控制 当用户对某一区域没有权限时,也不给用户显示。也可以是用户未登录时显示登陆,登陆后显示注销。
这就需要Thymeleaf和Security的整合,还需要添加一个依赖:
<dependency > <groupId > org.thymeleaf.extras</groupId > <artifactId > thymeleaf-extras-springsecurity5</artifactId > <version > 3.0.4.RELEASE</version > </dependency >
因为需要操作前端页面,所以还需要导入命名空间:
<html lang ="en" xmlns:th ="http://www.thymeleaf.org" xmlns:sec ="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5" >
但官方推荐使用:
<html lang ="en" xmlns:th ="http://www.thymeleaf.org" xmlns:sec ="http://www.thymeleaf.org/extras/spring-security" >
使用官方的会有提示!
登录与注销 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 <!DOCTYPE html > <html lang ="en" xmlns:th ="http://www.thymeleaf.org" xmlns:sec ="http://www.thymeleaf.org/extras/spring-security" > <head > <meta charset ="UTF-8" > <title > Title</title > </head > <body > <hr > <div sec:authorize ="!isAuthenticated()" > //isAuthenticated()判断是否登录 <a href ="" > 登录</a > <a href ="" > 注册</a > </div > <div sec:authorize ="isAuthenticated()" > 用户名:<span sec:authentication ="name" > </span > 角 色:<span sec:authentication ="principal.authorities" > </span > </div > //principal.authorities 获取角色 <div sec:authorize ="isAuthenticated()" > <a th:href ="@{/logout}" > 注销</a > </div > <a th:href ="@{/div1/1}" > 1-1</a > <br > <a th:href ="@{/div2/1}" > 2-1</a > <br > <a th:href ="@{/div3/1}" > 3-1</a > <br > </body > </html >
页面显示 <div sec:authorize ="hasRole('div1')" > </div >
如果有角色div1,则div显示。
关闭Csrf功能 默认是打开的。在授权中加入。