为什么要学SpringSecurity SpringSecurity的竞争对手是Apache的Shiro框架,并且市面上用Shir的公司要更多,但是在SpringCloud微服务中推荐用SpringSecurity框架做安全控制。
什么是SpringSecurity SpringSecurity是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。
SpringSecurity应用场景 Security在很多企业中作为后台角色权限框架、授权认证oauth2.0、安全防护(防止跨站点请求)、Session攻击、非常容易融合SpringMVC使用等
比如有两个账户admin和zhangsan,admin账户所有请求都有访问权限,zhangsan账户只能访问查询和添加订单权限,如果框架返回403表示权限不足,401表示没有授权
SpringBoot项目集成Security 环境搭建 导入POM依赖 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 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 <?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 > <groupId > cn.itcast</groupId > <artifactId > springboot-security-demo</artifactId > <version > 0.0.1-SNAPSHOT</version > <name > springboot-security-demo</name > <description > Demo project for Spring Boot</description > <parent > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-parent</artifactId > <version > 2.0.1.RELEASE</version > </parent > <dependencyManagement > <dependencies > <dependency > <groupId > org.springframework.cloud</groupId > <artifactId > spring-cloud-dependencies</artifactId > <version > Finchley.M7</version > <type > pom</type > <scope > import</scope > </dependency > </dependencies > </dependencyManagement > <dependencies > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-web</artifactId > </dependency > <dependency > <groupId > org.projectlombok</groupId > <artifactId > lombok</artifactId > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-freemarker</artifactId > </dependency > </dependencies > <repositories > <repository > <id > spring-milestones</id > <name > Spring Milestones</name > <url > https://repo.spring.io/libs-milestone</url > <snapshots > <enabled > false</enabled > </snapshots > </repository > </repositories > <build > <plugins > <plugin > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-maven-plugin</artifactId > </plugin > </plugins > </build > </project >
导入application.yml配置文件
这里页面用到了freemarker,所以要配置一下,不会freemarker也没关系。当做html页面按照教程配置就行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 spring: freemarker: suffix: .ftl content-type: text/html charset: UTF-8 cache: false template-loader-path: - classpath:/templates mvc: static-path-pattern: /static/**
添加OrderController 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 36 37 38 39 40 41 42 43 44 package cn.itcast.springbootsecuritydemo.controller;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;@Controller public class OrderController { @RequestMapping ("/" ) public String index () { return "index" ; } @RequestMapping ("/showOrder" ) public String showOrder () { return "showOrder" ; } @RequestMapping ("/addOrder" ) public String addOrder () { return "addOrder" ; } @RequestMapping ("/updateOrder" ) public String updateOrder () { return "updateOrder" ; } @RequestMapping ("/deleteOrder" ) public String deleteOrder () { return "deleteOrder" ; } @GetMapping ("/login" ) public String login () { return "login" ; } }
在resources目录下的templates目录添加模板页
index.ftl
1 2 3 4 5 6 7 8 9 <h1 > 订单系统</h1 > <br > <a href ="showOrder" > 查询订单</a > <br > <a href ="addOrder" > 添加订单</a > <br > <a href ="deleteOrder" > 删除订单</a > <br > <a href ="updateOrder" > 修改订单</a >
login.ftl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd" > <html > <head > <meta http-equiv ="Content-Type" content ="text/html; charset=ISO-8859-1" > <title > Insert title here</title > </head > <body > <h1 > 每特教育--权限控制登陆系统</h1 > <form action ="/login" method ="post" > <span > 用户名称</span > <input type ="text" name ="username" /> <br > <span > 用户密码</span > <input type ="password" name ="password" /> <br > <input type ="submit" value ="登陆" > </form > <#if RequestParameters ['error ']??> 用户名称或者密码错误 </#if > </body > </html >
addOrder.ftl
deleteOrder.ftl
showOrder.ftl
updateOrder.ftl
logFail.ftl
使用HttpBasic模式登陆 什么是Basic认证 在HTTP协议进行通信的过程中,HTTP协议定义了基本认证过程以允许HTTP服务器对WEB浏览器进行用户身份证的方法,当一个客户端向HTTP服务 器进行数据请求时,如果客户端未被认证,则HTTP服务器将通过基本认证过程对客户端的用户名及密码进行验证,以决定用户是否合法。客户端在接收到HTTP服务器的身份认证要求后,会提示用户输入用户名及密码,然后将用户名及密码以BASE64加密,加密后的密文将附加于请求信息中, 如当用户名为mayikt,密码为:123456时,客户端将用户名和密码用“:”合并,并将合并后的字符串用BASE64加密为密文,并于每次请求数据 时,将密文附加于请求头(Request Header)中。HTTP服务器在每次收到请求包后,根据协议取得客户端附加的用户信息(BASE64加密的用户名和密码),解开请求包,对用户名及密码进行验证,如果用 户名及密码正确,则根据客户端请求,返回客户端所需要的数据;否则,返回错误代码或重新要求客户端提供用户名及密码。
添加SpringSecurity依赖 1 2 3 4 5 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-security</artifactId > </dependency >
添加SpringSecurity配置文件
本节知识点主要介绍Basic认证,下面配置文件不做详细介绍,后续介绍。
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 36 37 38 39 40 41 42 package cn.itcast.springbootsecuritydemo.config;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;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.password.NoOpPasswordEncoder;@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure (AuthenticationManagerBuilder auth) throws Exception { auth .inMemoryAuthentication() .withUser("admin" ) .password("123456" ) .authorities("addOrder" ); } protected void configure (HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/**" ) .fullyAuthenticated() .and() .httpBasic(); } @Bean public static NoOpPasswordEncoder passwordEncoder () { return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance(); } }
效果展示
使用FromLogin模式登陆
表单登陆这种方式在实际开发中会用的更多一点
修改配置
修改配置文件中的configure方法,如下
1 2 3 4 5 6 7 8 9 protected void configure (HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/**" ) .fullyAuthenticated() .and() .formLogin(); }
效果展示
SpringSecurity框架会提供一个默认的登陆页面,也可以是使用我们自己的
权限控制
打开localhost:8080登录成功后发现每一个功能都能访问,原因是没有配置控制权限
修改配置信息
修改SecurityConfig.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 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 package cn.itcast.springbootsecuritydemo.config;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;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.password.NoOpPasswordEncoder;@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure (AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication() .withUser("admin" ) .password("123456" ) .authorities("showOrder" ,"addOrder" ,"updateOrder" ,"deleteOrder" ); auth.inMemoryAuthentication(). withUser("userAdd" ) .password("123456" ) .authorities("showOrder" ,"addOrder" ); } protected void configure (HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/showOrder" ).hasAnyAuthority("showOrder" ) .antMatchers("/addOrder" ).hasAnyAuthority("addOrder" ) .antMatchers("/updateOrder" ).hasAnyAuthority("updateOrder" ) .antMatchers("/deleteOrder" ).hasAnyAuthority("deleteOrder" ) .antMatchers("/**" ).fullyAuthenticated().and().formLogin(); } @Bean public static NoOpPasswordEncoder passwordEncoder () { return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance(); } }
使用admin帐号登陆效果
每一个页面都有权限访问
使用userAdd帐号登陆效果
查询和添可以访问,删除和修改提示权限不足
修改403页面 分析 查看报错页面,发现错误原因是因为/error 没有对应的映射路径
然后查看控制台找到/error对应的控制器
查看BasicErrorController控制器中的源代码,发现error视图在errorHtml方法里,观察status.value()的值是403
编写配置文件覆盖SpringBoot默认控制器 我们可以覆盖SpringBoot提供的默认的错误页面,根据不同的错误状态码,跳转的不同的页面
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 package cn.itcast.springbootsecuritydemo.config;import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;import org.springframework.boot.web.server.ErrorPage;import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.http.HttpStatus;@Configuration public class WebServerAutoConfiguration { @Bean public ConfigurableServletWebServerFactory webServerFactory () { TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory(); ErrorPage errorPage400 = new ErrorPage(HttpStatus.BAD_REQUEST, "/error/400" ); ErrorPage errorPage401 = new ErrorPage(HttpStatus.UNAUTHORIZED, "/error/401" ); ErrorPage errorPage403 = new ErrorPage(HttpStatus.FORBIDDEN, "/error/403" ); ErrorPage errorPage404 = new ErrorPage(HttpStatus.NOT_FOUND, "/error/404" ); ErrorPage errorPage415 = new ErrorPage(HttpStatus.UNSUPPORTED_MEDIA_TYPE, "/error/415" ); ErrorPage errorPage500 = new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/error/500" ); factory.addErrorPages(errorPage400, errorPage401, errorPage403, errorPage404, errorPage415, errorPage500); return factory; } }
编写错误页面对应的控制器 1 2 3 4 5 6 7 8 9 10 11 12 13 package cn.itcast.springbootsecuritydemo.controller;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;@Controller @RequestMapping ("/error" )public class ErrorController { @RequestMapping ("/403" ) public String statusCode403 () { return "/error/403" ; } }
添加403错误页面
在templates目录下新建error目录,error目录下新建403模板页面
403.ftl
效果