前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >springboot(19)-security

springboot(19)-security

作者头像
叔牙
发布2020-11-19 15:19:11
4930
发布2020-11-19 15:19:11
举报
文章被收录于专栏:一个执拗的后端搬砖工

springboot&security

Spring Security是一种功能强大、高度可定制的身份验证和访问控制框架。这也是是保护基于Spring的应用程序的标准。

Spring Security是一个专注于向Java应用程序提供身份验证和授权的框架。与所有的Spring项目一样,Spring Security的真正功能在于它可以容易地扩展以满足定制需求。

特性

  • 认证和授权的全面和可扩展的支持
  • 防止攻击,如会话固定、点击劫持、跨站点请求伪造等
  • Servlet API集成
  • 与Spring Web MVC的可选集成
  • 更多比如和springboot应用集成,与Auth2集成,与jwt集成等等

本篇文章中我们将基于springboot整合spring security5。

目标

基于springboot2.x集成spring security5,实现应用资源的保护:

  • 用户登陆后才能访问服务端资源
  • 拥有特定权限后才能访问受保护资源

技术实现

介于springboot和spring security都是一家的产品,在融合过程中存在天然的优势,基于以上目标,我们大致有一下几点需要注意:

  • 登录接口不需要保护
  • 登录成功和失败都需要有相应的跳转页面
  • 访问受保护资源受限后跳转无权页面

具体底层技术,我们基于springboot2.x+spring security5 +Thymeleaf来实现。

1:引入依赖

除了引入springboot应用所需要的基础依赖之外,还要引入security和thymeleaf依赖:

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

2:修改配置文件

修改application.properties主配置文件用以支持Thymeleaf:

# thymeleaf spring.thymeleaf.prefix=classpath:/templates/ spring.thymeleaf.suffix=.html spring.thymeleaf.mode=HTML spring.thymeleaf.encoding=UTF-8 spring.thymeleaf.content-type=text/html spring.thymeleaf.cache=false logging.level.org.springframework.security: INFO management.endpoints.web.exposure.include=*

3:编写认证管理类

spring security对于用户的登录认证支持两种模式:

  • 内存认证:将用户、密码以及对应的权限存放到内存中,暴露InMemoryUserDetailsManager实例注册到spring容器中,来实现用户登录及权限认证
  • 数据库认证:大多数商业应用都是采用数据库认证,应用中自己实现UserDetailService或者使用JdbcUserDetailsManager来实现数据库认证。
  • 此处为了方便起见,我们使用内存认证,暴露InMemoryUserDetailsManager实例到容器中:

@Order(Ordered.HIGHEST_PRECEDENCE) @Configuration public class AuthenticationSecurity { @SuppressWarnings("deprecation") @Bean public InMemoryUserDetailsManager inMemoryUserDetailsManager() throws Exception { return new InMemoryUserDetailsManager( User.withDefaultPasswordEncoder().username("admin").password("admin") .roles("ADMIN", "USER", "ACTUATOR").build(), User.withDefaultPasswordEncoder().username("user").password("user") .roles("USER").build()); } }

这段代码的意思是,我们使用InMemoryUserDetailsManager来实现用户权限认证,初始化了两个用户并设置了相应的密码和权限。

4:配置权限适配

在控制权限的时候,我们需要对一些接口或者url开白名单,比如登录接口如果加权限的话,就永远登录不了了,还有一些认证成功或失败后的url跳转,包括静态资源的过滤。

@Configuration @EnableGlobalMethodSecurity(prePostEnabled =true) public class CustomApplicationSecurity extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { // @formatter:off http.authorizeRequests() .antMatchers("/login").permitAll() .anyRequest().fullyAuthenticated() .and() .formLogin().loginPage("/login").failureUrl("/login?error") .and() .logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout")) .and() .exceptionHandling().accessDeniedPage("/access?error"); // @formatter:on } @Override public void configure(WebSecurity web) throws Exception { web.ignoring().antMatchers("/config/**", "/css/**", "/fonts/**", "/img/**", "/js/**"); } }

自定义配置类并继承WebSecurityConfigurerAdapter重写configure方法:

  • 设置登录接口所有人都可以访问
  • 其他接口都需要认证后才能访问
  • 使用表单登录,指定登录url,并且指定登录失败后的跳转
  • 指定登出路径
  • 指定认证失败后的跳转页面
  • 对于静态资源的访问不做权限管控

5:开启Security功能

在应用启动类上增加注解@EnableWebSecurity:

@SpringBootApplication @EnableWebSecurity public class App { public static void main(String[] args) { SpringApplication.run(App.class, args); } }

6:编写请求处理器

@Controller public class HomeController { @GetMapping("/") @PreAuthorize("hasRole('ADMIN')") public String home(Map<String, Object> model) { model.put("message", "Hello World"); model.put("title", "Hello Home"); model.put("date", new Date()); return "home"; } @GetMapping("/login") public String index() { return "login"; } }

7:编写页面文件

  • 登录主页面

<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <title>Login</title> <link rel="stylesheet" th:href="@{/css/bootstrap.min.css}" href="../../css/bootstrap.min.css" /> </head> <body onload="document.f.username.focus();"> <div class="container"> <div class="navbar"> <div class="navbar-inner"> <a class="brand" href="http://www.thymeleaf.org"> Thymeleaf - Plain </a> <ul class="nav"> <li><a th:href="@{/}" href="home.html"> Home </a></li> </ul> </div> </div> <div class="content"> <p th:if="${param.logout}" class="alert">You have been logged out</p> <p th:if="${param.error}" class="alert alert-error">There was an error, please try again</p> <h2>Login with Username and Password</h2> <form name="form" th:action="@{/login}" action="/login" method="POST"> <fieldset> <input type="text" name="username" value="" placeholder="Username" /> <input type="password" name="password" placeholder="Password" /> </fieldset> <input type="submit" id="login" value="Login" class="btn btn-primary" /> </form> </div> </div> </body> </html>

  • 错误页面

<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <title>Error</title> <link rel="stylesheet" th:href="@{/css/bootstrap.min.css}" href="../../css/bootstrap.min.css" /> </head> <body> <div class="container"> <div class="navbar"> <div class="navbar-inner"> <a class="brand" href="http://www.thymeleaf.org"> Thymeleaf - Plain </a> <ul class="nav"> <li><a th:href="@{/}" href="home.html"> Home </a></li> <li><a th:href="@{/logout}" href="logout"> Logout </a></li> </ul> </div> </div> <h1 th:text="${title}">Title</h1> <div id="created" th:text="${#dates.format(timestamp)}">July 11, 2012 2:17:16 PM CDT</div> <div> There was an unexpected error (type=<span th:text=" </div> <div th:text="${message}">Fake content</div> <div> Please contact the operator with the above information. </div> </div> </body> </html>

  • 登录成功后主页面

<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <title th:text="${title}">Title</title> <link rel="stylesheet" th:href="@{/css/bootstrap.min.css}" href="../../css/bootstrap.min.css" /> </head> <body> <div class="container"> <div class="navbar"> <div class="navbar-inner"> <a class="brand" href="http://www.thymeleaf.org"> Thymeleaf - Plain </a> <ul class="nav"> <li><a th:href="@{/}" href="home.html"> Home </a></li> <li><a th:href="@{/logout}" href="logout"> Logout </a></li> </ul> </div> </div> <h1 th:text="${title}">Title</h1> <div th:text="${message}">Fake content</div> <div id="created" th:text="${#dates.format(date)}">July 11, 2012 2:17:16 PM CDT</div> </div> </body> </html>

  • 访问受限后跳转页面

<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <title>Error</title> <link rel="stylesheet" th:href="@{/css/bootstrap.min.css}" href="../../css/bootstrap.min.css" /> </head> <body> <div class="container"> <div class="navbar"> <div class="navbar-inner"> <a class="brand" href="http://www.thymeleaf.org"> Thymeleaf - Plain </a> <ul class="nav"> <li><a th:href="@{/}" href="home.html"> Home </a></li> <li><a th:href="@{/logout}" href="logout"> Logout </a></li> </ul> </div> </div> <h1 th:text="${title}">Title</h1> <p class="alert alert-error">Access denied: you do not have permission for that resource</p> <div th:text="${message}">Fake content</div> <div>Please contact the operator with the above information.</div> </div> </body> </html>

测试

启动应用后,浏览器输入localhost:8080/ :

故意输入一个错误的账号密码:

输入普通用户账号和密码:

登录成功了,但是跳转的时候接口有做权限管控,需要ADMIN角色:

输入admin账号和密码:

访问成功。到这里我们也就实现了springboot集成security来实现简单的权限管控。

总结

目前市面上流行的权限管控框架有很多种,比较常用的就是shiro和security,shiro使用起来更简单,security与spring应用能够无缝融合,两者孰好孰坏并没有定论,具体使用哪个,完全取决于开发人员的 技术站或者应用的历史原因。 此篇通过分析和使用代码的方式实现了简单的应用访问权限管控,如果是简单的应用,对于上述的代码把认证管理工具改成数据库的方式就能直接使用,具体的security实现原理和核心类的架构依赖本篇不做赘述。 希望给大家带来一定的参考价值。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2018-11-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 PersistentCoder 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档