用户角色权限
对于Web开发来说,包括后边要用到的REST风格,权限就是基于用户的身份,能够访问哪些URL,因为在REST风格下URL意味做一个动作然后获得结果。
按照教程,目前我们已经在Spring Security的配置类里配置了一些角色如下:
姓名 |
角色 |
jenny |
ADMIN, MANAGER |
minko |
MANAGER |
cony |
EMPLOYEE |
现在要实现一个功能,就是对一些URL,仅限有相应权限的人员才能访问。对应关系如下:
角色 |
可访问路径 |
解释 |
EMPLOYEE |
/ |
表示全部页面都需要具有EMPLOYEE身份,即需要登录 |
MANAGER |
/manager/** |
访问/manager/下的路径需要MANAGER角色 |
ADMIN |
/system/** |
访问/system/下的路径需要ADMIN角色 |
很显然,这是一个有层级的权限分工体系,想要实现这个目的,首先必须所有人都是EMPLOYEE角色,然后在此基础上再区分出不同的角色对应不同的URL。
开发的步骤如下:
- 在配置文件中给各个用户定义好角色
- 在配置文件中定义好各个角色对应的路径
- 编写对应每个路径的控制器和视图
- 登录后访问特定路径进行实验
先来第一步,要将用户角色重新定义一下,这个很像类,即经理,系统管理员都继承自员工。
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
User.UserBuilder users = User.withDefaultPasswordEncoder();
auth.inMemoryAuthentication()
.withUser(users.username("jenny").password("test123").roles("EMPLOYEE", "MANAGER", "ADMIN"))
.withUser(users.username("minko").password("test123").roles("EMPLOYEE", "MANAGER"))
.withUser(users.username("cony").password("test123").roles("EMPLOYEE"));
}
定义好之后,所有的员工都是EMPLOYEE角色,然后还有不同的具体角色。
之后来配置路径与角色的关系,找到另外一个配置方法:
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin().loginPage("/showMyLoginPage").loginProcessingUrl("/authenticateTheUser")
.permitAll()
.and()
.logout().permitAll();
}
把红色的部分删除,红色部分表示对任何部分都需要验证,但只是通用的验证,通过登录验证就可以访问,现在需要把这一块修改成更加具体的内容:
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/").hasRole("EMPLOYEE")
.antMatchers("/manager/**").hasRole("MANAGER")
.antMatchers("/system/**").hasRole("ADMIN")
.and()
.formLogin().loginPage("/showMyLoginPage").loginProcessingUrl("/authenticateTheUser")
.permitAll()
.and()
.logout().permitAll();
}
.antMatchers("/").hasRole("EMPLOYEE")
用来配置一个URL与一个角色之间的关系,比如这里就配置了EMPLOYEE角色才能访问"/"路径,之后配置了MANAGER角色访问/manger路径下的所有路径,ADMIN角色则对应/system下的所有路径。
在配置好之后,就来编写对应的控制器和视图。为了方便测试,再给登录后的首页也添加两个链接,由于这些控制器和页面都比较简单,就直接放代码了:
控制器:
package cc.conyli.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class HomepageController {
@RequestMapping("/")
public String Homepage() {
return "home";
}
@RequestMapping("/manager")
public String showManagerpage() {
return "manager";
}
@RequestMapping("/system")
public String showAdminPage() {
return "system";
}
}
manager.jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Manager Page</title>
</head>
<body>
<h1>This is page only for managers.</h1>
<p><a href="${pageContext.request.contextPath}/">Back to Main</a></p>
</body>
</html>
system.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>System Page</title>
</head>
<body>
<h1>This is admin page only for system administrators</h1>
<p><a href="${pageContext.request.contextPath}/">Back to Main</a></p>
</body>
</html>
首页添加两个链接:
<p><a href="${pageContext.request.contextPath}/manager">Managers only</a></p>
<p><a href="${pageContext.request.contextPath}/system">System only</a></p>
然后启动项目来测试一下。这里很神奇的发现,不能使用/manager路径,会触发Tomcat的登录机制。所以就更改一下路径,把/manager路径更改成/leader,相关代码也修改一下,这里就省略了。
再重新启动项目,可以看到,不登录的话访问任何链接都会要求登录。使用cony登录后,点两个链接都会报403错误,使用minko登录,只能点开manager的链接,system依然会报403,而使用jenny登录,就全部链接都可以访问了。
自定义错误页面
成功的按照角色配置了访问URL的权限,现在还有一个问题就是直接返回403错误对于开发者可以知道,但对于一般用户不是太友好。要看一下如何自定义页面。
实际上依然是配置类,在之前使用配置类自定义了登录页面,那就会想到是不是也可以定义其他页面呢。实际上是可以的,错误相当于一个异常,只要定义一下异常处理的页面就可以了。(JSP中也有类似技术)
先来修改配置类:
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/").hasRole("EMPLOYEE")
.antMatchers("/leader/**").hasRole("MANAGER")
.antMatchers("/system/**").hasRole("ADMIN")
.and()
.formLogin().loginPage("/showMyLoginPage").loginProcessingUrl("/authenticateTheUser")
.permitAll()
.and()
.logout().permitAll()
.and()
.exceptionHandling().accessDeniedPage("/access-denied/");
}
添加了最后两行内容,就是抛出异常的处理,然后定向到对应的URL,剩下就是编写控制器和页面了,比较简单:
@RequestMapping("/access-denied/")
public String accessDenied() {
return "denied";
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>权限不够</title>
</head>
<body>
<h1>权限不够,请 <a href="${pageContext.request.contextPath}/">退出</a></h1>
</body>
</html>
再运行项目,访问没有权限的路径的时候,就会看到这个页面的提示,注意此时浏览器中的路径还是目标路径而不是这个/access-denied/路径,这样就解决了这个问题。
根据角色展示不同内容
权限相关的还有一块内容,除了以URL控制,直接限定用户访问的资源以外,在一个页面里,也可以限定不同角色访问的资源。这样就更加灵活了。
像刚才的项目,我们让所有用户都可以去点manger和admin的两个链接,但在实际项目中,根本不需要对无权限用户展示相应的链接。
这里依然要使用Spring Security JTL,home.jsp已经引入了SSJTL,只要用如下方式来环绕两个链接即可:
<security:authorize access="hasRole('MANAGER')">
<p><a href="${pageContext.request.contextPath}/leader">Managers only</a></p>
</security:authorize>
<security:authorize access="hasRole('ADMIN')">
<p><a href="${pageContext.request.contextPath}/system">System only</a></p>
</security:authorize>
这里使用的标签是<security:authorize
,用access="hasRole(***)"
属性来控制。将这个标签围绕在需要对应权限展示的内容就可以完成控制了。