本文以Spring Boot Thymeleaf为例,用Spring Security 保护 /admin 和 /user 页面
本例涉及的技术:
1. Spring Boot 1.5.6.REALEASE
2. Spring 4.3.8.REALEASE
3. Spring Security 4.2.2
4. Thymeleaf 2.2.5.REALEASE
5. Tomcat embed 8.5.14
6. Maven 3
7. Java 8
1. 项目目录结构
2. 项目依赖 pom.xml
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.thinkingingis</groupId>
<artifactId>spring-boot-security</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>spring-boot-security</name>
<url>http://maven.apache.org</url>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!-- Spring Security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity4</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>bootstrap</artifactId>
<version>3.3.7</version>
</dependency>
</dependencies>
<build>
<plugins>
<!-- Package as an executable jar/war -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
3. Spring Security
3.1 继承自WebSecurityConfigurerAdapter 同时在configure方法中定义了安全角色
对于admin(管理员)角色来说:
a. 可以访问/admin.html页面
b. 不能访问/user.html页面,并重定向到403页面
对于user(用户)角色来说:
a.可以访问/user.html页面
b.不能访问/admin.html页面,并重定向到403页面
SpringSecurityConfig.java
@Configuration
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private AccessDeniedHandler accessDeniedHandler;
protected void configure(HttpSecurity http) throws Exception{
http.csrf().disable()
.authorizeRequests()
.antMatchers("/", "/home", "/about").permitAll()
.antMatchers("/admin/**").hasAnyRole("ADMIN")
.antMatchers("/user/**").hasAnyRole("USER")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll()
.and()
.exceptionHandling().accessDeniedHandler(accessDeniedHandler);
}
//create two users admin and user
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception{
auth.inMemoryAuthentication()
.withUser("user").password("password").roles("USER")
.and()
.withUser("admin").password("password").roles("ADMIN");
}
}
3.2 定义403无权限访问的处理,重定向到/403页面
MyAccessDeniedHandler.java
@Component
public class MyAccessDeniedHandler implements AccessDeniedHandler {
private static Logger logger = LoggerFactory.getLogger(MyAccessDeniedHandler.class);
@Override
public void handle( HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse,
AccessDeniedException e)
throws IOException, ServletException {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if(auth != null) {
logger.info("User '" + auth.getName() + "' attempted to access the protected URL: " + httpServletRequest.getRequestURI());
}
httpServletResponse.sendRedirect(httpServletRequest.getContextPath() + "/403");
}
}
4. Spring Boot
4.1 DefaultController.java
定义http请求和视图名
@Controller
public class DefaultController {
@GetMapping("/")
public String index() {
return "/home";
}
@GetMapping("/home")
public String home() {
return "/home";
}
@GetMapping("/admin")
public String admin() {
return "/admin";
}
@GetMapping("/user")
public String user() {
return "/user";
}
@GetMapping("/about")
public String about() {
return "/about";
}
@GetMapping("/login")
public String login() {
return "/login";
}
@GetMapping("/403")
public String error403() {
return "/error/403";
}
}
4.2 Spring Boot的启动程序
SpringBootWebApplication.java
@SpringBootApplication
public class SpringBootWebApplication {
public static void main(String[] args) throws Exception {
SpringApplication.run(SpringBootWebApplication.class, args);
}
}
5.Thymeleaf及静态资源
对于 thymeleaf 文件,均放到 src/main/resources/templates/目录下
header.html
<html xmlns:th="http://www.thymeleaf.org">
<head>
<div th:fragment="header-css">
<link rel="stylesheet" type="text/css" href="webjars/bootstrap/3.3.7/css/bootstrap.min.css" />
<link rel="stylesheet" th:href="@{/main.css}"/>
</div>
</head>
<body>
<div th:fragment="header">
<!-- this is header -->
<nav class="navbar navbar-inverse">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" th:href="@{/}">ThinkingInGIS</a>
</div>
<div id="navbar" class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<li class="active"><a th:href="@{/}">Home</a></li>
</ul>
</div>
</div>
</nav>
</div>
footer.html
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
<head>
</head>
<body>
<div th:fragment="footer">
<div class="container">
<footer>
<!-- this is footer -->
© 2017 ThinkingInGIS
<span sec:authorize="isAuthenticated()">
| Logged user: <span sec:authentication="name"></span> |
Roles: <span sec:authentication="principal.authorities"></span> |
<a th:href="@{/logout}">登出</a>
</span>
</footer>
</div>
</div>
</body>
</html>
home.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Spring Boot Thymeleaf + Spring Security</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<link rel="stylesheet" type="text/css" href="webjars/bootstrap/3.3.7/css/bootstrap.min.css"/>
<link rel="stylesheet" type="text/css" th:href="@{/main.css}"/>
<script src="https://cdn.bootcss.com/jquery/3.2.1/jquery.js"></script>
</head>
<body>
<div th:replace="fragments/header :: header"></div>
<div class="container">
<div class="starter-template">
<h1>Spring Boot + Thymeleaf + Spring Security 示例</h1>
<h2>1. 打开 <a th:href="@{/admin}">管理员页面 (受 Spring Security 保护, 需要管理员权限)</a></h2>
<h2>2. 打开 <a th:href="@{/user}">用户页面 (受 Spring Security 保护, 需要用户权限)</a></h2>
<h2>3. 打开 <a th:href="@{/about}">游客页面</a></h2>
</div>
</div>
<div th:replace="fragments/footer :: footer"></div>
</body>
</html>
admin.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<link rel="stylesheet" type="text/css" href="webjars/bootstrap/3.3.7/css/bootstrap.min.css"/>
<script type="text/javascript" src="webjars/jquery/2.2.4/jquery.min.js"></script>
</head>
<body>
<div class="container">
<div class="starter-template">
<h1>GORGEOUS! 管理员页面 (受 Spring Security 保护, 需要管理员权限)</h1>
<h1 th:inline="text">Hello [[${#httpServletRequest.remoteUser}]]!</h1>
<form th:action="@{/logout}" method="post">
<input type="submit" class="btn btn-danger" value="登出"/>
</form>
</div>
</div>
<div class="container">
<footer>
<p>
© <a >ThinkingInGIS</a> 2017
</p>
</footer>
</div>
</body>
</html>
user.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<link rel="stylesheet" type="text/css" href="webjars/bootstrap/3.3.7/css/bootstrap.min.css"/>
<script type="text/javascript" src="webjars/jquery/2.2.4/jquery.min.js"></script>
</head>
<body>
<div class="container">
<div class="starter-template">
<h1>普通用户页面 (受 Spring Security 保护, 需要用户权限)</h1>
<h1 th:inline="text">Hello [[${#httpServletRequest.remoteUser}]]!</h1>
<form th:action="@{/logout}" method="post">
<input type="submit" class="btn btn-danger" value="登出"/>
</form>
</div>
</div>
<div class="container">
<footer>
<p>
© <a >ThinkingInGIS</a> 2017
</p>
</footer>
</div>
</body>
</html>
about.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<link rel="stylesheet" type="text/css" href="webjars/bootstrap/3.3.7/css/bootstrap.min.css"/>
<script type="text/javascript" src="webjars/jquery/2.2.4/jquery.min.js"></script>
</head>
<body>
<div class="container">
<div class="starter-template">
<h1>游客页面 无需登录</h1>
</div>
</div>
<div class="container">
<footer>
<p>
© <a >ThinkingInGIS</a> 2017
</p>
</footer>
</div>
</body>
</html>
login.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
<link rel="stylesheet" type="text/css" href="webjars/bootstrap/3.3.7/css/bootstrap.min.css"/>
<script type="text/javascript" src="webjars/jquery/2.2.4/jquery.min.js"></script>
</head>
<body>
<div th:replace="fragments/header :: header"></div>
<div class="container">
<div class="row" style="margin-top:20px">
<div class="col-xs-12 col-sm-8 col-md-6 col-sm-offset-2 col-md-offset-3">
<form th:action="@{/login}" method="post">
<fieldset>
<h1>登录</h1>
<div th:if="${param.error}">
<div class="alert alert-danger">
Invalid username and password.
</div>
</div>
<div th:if="${param.logout}">
<div class="alert alert-info">
You have been logged out.
</div>
</div>
<div class="form-group">
<input type="text" name="username" id="username" class="form-control input-lg"
placeholder="用户名" required="true" autofocus="true"/>
</div>
<div class="form-group">
<input type="password" name="password" id="password" class="form-control input-lg"
placeholder="密码" required="true"/>
</div>
<div class="row">
<div class="col-xs-6 col-sm-6 col-md-6">
<input type="submit" class="btn btn-lg btn-primary btn-block" value="登录"/>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
</div>
</div>
</fieldset>
</form>
</div>
</div>
</div>
<div th:replace="fragments/footer :: footer"></div>
</body>
</html>
403.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<title>403</title>
<link rel="stylesheet" type="text/css" href="webjars/bootstrap/3.3.7/css/bootstrap.min.css"/>
<script type="text/javascript" src="webjars/jquery/2.2.4/jquery.min.js"></script>
</head>
<body>
<div th:replace="fragments/header :: header"></div>
<div class="container">
<div class="starter-template">
<h1>403 - 没有访问权限</h1>
<div th:inline="text">Hello '[[${#httpServletRequest.remoteUser}]]',
你没有权限访问此页面.</div>
</div>
</div>
<!-- /.container -->
<div th:replace="fragments/footer :: footer"></div>
</body>
</html>
6.启动程序
6.1 /admin 下面的需要用admin用户登录才能访问
6.2 启动程序,访问 http://localhost:8080/
6.3 访问http://localhost:8080/admin 会被重定向到 http://localhost:8080/login
6.4 当输入无效的用户名和密码后...
6.5 用户名输入admin 密码输入 password 登录,页面会进入到 http://localhost:8080/admin
6.6 输入http://localhost:8080/user 会被重定向到 http://localhost:8080/403 最下面显示了登录的角色及用户名
6.7 点击 登出 会重定向到http://localhost:8080/login?logout
最后,自己试试 用 'user' 访问admin页面可看会有上面结果吧。
源码地址:https://github.com/ThinkingInGIS/spring-boot-security.git
至此,一个简单的spring boot + thymeleaf + spring security 程序 就搭建好了。
(如遇到问题,请留言给作者,以便共同探讨gis知识。[email protected])
更多干货 欢迎关注微信公众号: ThinkingInGIS
如果觉得本文对你有帮助,是可以赞赏作者的哦