今天我们将研究基于Spring Security的基于角色的访问和授权示例。但是,在阅读本篇文章之前,请先阅读我之前关于“Spring 4 Security MVC登录注销示例”的文章,以获取关于Spring 4 Security的一些基本知识。
Spring Security角色
在本文中,我们将讨论如何在Spring Web应用程序中定义、使用和管理类似“USER”、“ADMIN”等的Spring Security角色。与我之前的文章一样,本文示例也使用了Spring 4 MVC Security与内存存储和Spring Java配置功能来开发应用程序。这意味着我们不会使用web.xml文件,也不会编写一行Spring XML配置。我们将使用“In-Memory Store”选项来存储和管理用户凭据。我们将使用Spring 4.0.2.RELEASE、Spring STS 3.7 Suite IDE、Spring TC Server 3.1与Java 1.8和Maven构建工具来开发此示例。
Spring Security基于角色的访问授权示例
- 创建一个“Simple Spring Web Maven”项目在Spring STS Suite中,具体细节如下。
项目名称:SpringMVCSecruityMavenRolesApp2。使用我之前发布的相同pom.xml文件,并进行以下更改
<artifactId>SpringMVCSecruityMavenRolesApp</artifactId>
<build>
<finalName>SpringMVCSecruityMavenRolesApp</finalName>
</build>
</project>
- 使用我之前发布的所有Java和JSP文件。我们只讨论这里更新或新增的内容。
- 更新LoginSecurityConfig.java文件以配置用户角色,如“USER”和“ADMIN”。
LoginSecurityConfig.java
package com.journaldev.spring.secuity.config;
import org.springframework.beans.factory.annotation.Autowired;
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;
@Configuration
@EnableWebSecurity
public class LoginSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
public void configureGlobal(AuthenticationManagerBuilder authenticationMgr) throws Exception {
authenticationMgr.inMemoryAuthentication()
.withUser("jduser").password("jdu@123").authorities("ROLE_USER")
.and()
.withUser("jdadmin").password("jda@123").authorities("ROLE_USER","ROLE_ADMIN");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/homePage").access("hasRole('ROLE_USER') or hasRole('ROLE_ADMIN')")
.antMatchers("/userPage").access("hasRole('ROLE_USER')")
.antMatchers("/adminPage").access("hasRole('ROLE_ADMIN')")
.and()
.formLogin().loginPage("/loginPage")
.defaultSuccessUrl("/homePage")
.failureUrl("/loginPage?error")
.usernameParameter("username").passwordParameter("password")
.and()
.logout().logoutSuccessUrl("/loginPage?logout");
}
}
代码解释
- 在configureGlobal()方法中,我们添加了两个用户:一个用户具有“ROLE_USER”角色,另一个用户具有“ROLE_USER”和“ROLE_ADMIN”角色。这意味着第二个用户将作为管理员用户。我们可以配置任意数量的用户和角色。
- 我们可以使用authorities(ROLE)或roles(ROLE)方法来配置应用程序中的角色。
- authorities()和roles()方法之间的区别:
- authorities()需要完整的角色名称,如“ROLE_USER”
- roles()需要像“USER”这样的角色名称。它会自动将“ROLE_”值添加到这个“USER”角色名称。
- 在configure()方法中,我们定义了不同的URL,具有所需的访问角色。
antMatchers("/homePage")
.access("hasRole('ROLE_USER') or hasRole('ROLE_ADMIN')")
这段代码片段配置了“/homePage”对于USER和ADMIN角色都是可用的。
.antMatchers("/userPage").access("hasRole('ROLE_USER')")
.antMatchers("/adminPage").access("hasRole('ROLE_ADMIN')")
这段代码片段配置了“/userPage”仅由“USER”角色访问,而“/adminPage”仅由“ADMIN”角色访问。如果其他角色访问这些页面,我们将收到“403 Access is Denied”错误消息。
- 更新LoginController.java控制器文件以定义如下新的URL访问路径。
LoginController.java
package com.journaldev.spring.web.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class LoginController {
@RequestMapping(value = { "/"}, method = RequestMethod.GET)
public ModelAndView welcomePage() {
ModelAndView model = new ModelAndView();
model.setViewName("welcomePage");
return model;
}
@RequestMapping(value = { "/homePage"}, method = RequestMethod.GET)
public ModelAndView homePage() {
ModelAndView model = new ModelAndView();
model.setViewName("homePage");
return model;
}
@RequestMapping(value = {"/userPage"}, method = RequestMethod.GET)
public ModelAndView userPage() {
ModelAndView model = new ModelAndView();
model.setViewName("userPage");
return model;
}
@RequestMapping(value = {"/adminPage"}, method = RequestMethod.GET)
public ModelAndView adminPage() {
ModelAndView model = new ModelAndView();
model.setViewName("adminPage");
return model;
}
@RequestMapping(value = "/loginPage", method = RequestMethod.GET)
public ModelAndView loginPage(@RequestParam(value = "error",required = false) String error,
@RequestParam(value = "logout", required = false) String logout) {
ModelAndView model = new ModelAndView();
if (error != null) {
model.addObject("error", "Invalid Credentials provided.");
}
if (logout != null) {
model.addObject("message", "Logged out from JournalDEV successfully.");
}
model.setViewName("loginPage");
return model;
}
}
代码说明除了之前的示例之外,这里我们添加了两个新的URL。
-
“/userPage”被用户角色用于访问和执行普通用户活动。
-
“/adminPage”被管理员角色用于访问和执行管理员用户活动。管理员角色也可以访问”/userPage”URL。
-
更新homePage.jsp文件以提供用户和管理员角色特定的活动。
homePage.jsp
<%@taglib prefix="c" uri="https://java.sun.com/jsp/jstl/core"%>
<a href="${pageContext.request.contextPath}/userPage">JD User</a> | <a href="${pageContext.request.contextPath}/adminPage">JD Admin</a> | <a href="javascript:document.getElementById('logout').submit()">Logout</a>
<h3>Welcome to JournalDEV Tutorials</h3>
<ul>
<li>Java 8 tutorial</li>
<li>Spring tutorial</li>
<li>Gradle tutorial</li>
<li>BigData tutorial</li>
</ul>
<c:url value="/logout" var="logoutUrl" />
<form id="logout" action="${logoutUrl}" method="post" >
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
</form>
在这里,我们在顶部框架中添加了三个菜单选项。“注销”已在我之前的帖子中讨论过。新的两个链接是:
- JD用户:由“用户”和“管理员”角色都可以访问
- JD管理员:仅由“管理员”角色访问
注意:-在实时应用中,我们将只向“用户”角色显示“JD用户”链接并隐藏“JD管理员”链接。为了测试是否可以由“用户”角色访问以及查看确切的错误消息,我们没有隐藏此链接。20.添加新的adminPage.jsp文件,作为“管理员”角色的主页。
adminPage.jsp
<%@taglib prefix="c" uri="https://java.sun.com/jsp/jstl/core"%>
<h3>Welcome to JournalDEV Tutorials</h3>
<h3>Admin Page</h3>
<c:url value="/logout" var="logoutUrl" />
<form id="logout" action="${logoutUrl}" method="post" >
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
</form>
<c:if test="${pageContext.request.userPrincipal.name != null}">
<a href="javascript:document.getElementById('logout').submit()">Logout</a>
</c:if>
- 将新的 userPage.jsp 文件添加到“用户”角色的主页。
userPage.jsp
<%@taglib prefix="c" uri="https://java.sun.com/jsp/jstl/core"%>
<h3>Welcome to JournalDEV Tutorials</h3>
<h3>User Page</h3>
<c:url value="/logout" var="logoutUrl" />
<form id="logout" action="${logoutUrl}" method="post" >
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
</form>
<c:if test="${pageContext.request.userPrincipal.name != null}">
<a href="javascript:document.getElementById('logout').submit()">Logout</a>
</c:if>
我们已经完成了应用程序的开发。现在是时候查看我们项目的最终结构并测试应用程序了。最终项目结构如下:
Spring 安全角色示例应用程序测试
- 右键单击Spring STS IDE中的项目,然后选择“Run AS >> Run on Server”选项。
它将访问默认的应用程序欢迎页面,如下所示:3. 点击“Login to JournalDEV”链接。现在您已经位于登录页面。
5. 使用“USER”角色凭据首次登录:
用户名:jduser 密码:jdu@123现在我们将看到带有3个菜单选项的应用程序主页:“JD User”、“JD Admin”和“Logout”。点击“JD User”链接。由于我们是使用“USER”角色凭据登录应用程序,我们可以按照下面的步骤访问此链接。
只需在Spring STS IDE中使用向后箭头,然后这次点击“JD Admin”链接。
由于我们是使用“USER”角色凭据登录的,因此我们无法访问此链接。这就是为什么我们看到此错误消息的原因:“403 Access is denied”。9. 现在已注销,再次使用ADMIN角色凭据登录
用户名:jdadmin 密码:jda@123 这次我们可以成功访问“JD Admin”链接,如下所示。测试“Logout”链接以注销应用程序。
这是一个关于Spring Security角色示例,用于为Web应用页面提供授权访问。