springboot&security
Spring Security是一种功能强大、高度可定制的身份验证和访问控制框架。这也是是保护基于Spring的应用程序的标准。
Spring Security是一个专注于向Java应用程序提供身份验证和授权的框架。与所有的Spring项目一样,Spring Security的真正功能在于它可以容易地扩展以满足定制需求。
特性
本篇文章中我们将基于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对于用户的登录认证支持两种模式:
@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方法:
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实现原理和核心类的架构依赖本篇不做赘述。 希望给大家带来一定的参考价值。
本文分享自 PersistentCoder 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!