文章内容输出来源:拉勾教育Java高薪训练营
文章目录
说明
接着上一篇手写简易MVC第一篇,接着为这个框架增加权限的控制功能。
主要是配置请求方法能被哪些用户访问,如果某个用户没有权限则提示没有相应的权限。
这里的实现,直接按用户名进行判断,用户名在代码中硬编码。前端发送URL请求带上username就表示某个用户的访问。
思路说明
- 创建权限注解,权限注解在Controller类或者Controller类的method方法上标识
- 权限注解有个用户名数组,可以配置哪些用户参访问
- 在初始化构建Handler时,将请求所能访问的用户记录起来
- 在接收用户请求的处理中,再判断前端传过来的用户名参数是否在Handler所记录的用户数据中匹配,匹配的话则放行,不匹配的话则拒绝
实现过程
1. 创建权限注解Security
果在类上声明,则表示此类的所有handler都需要作权限过滤。如果在方法上声明,则表示只有此方法要进行权限过滤。如果某个类有标记,某个方法也有标记,则此方法的权限以方法上的注解配置为准
- 支持在类、方法上标识,
- 创建value数组属性,表示多个用户名(必填)
@Documented
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Security {
//用户名称数组
//配置了用户名称,则只能指定的用户访问。
String[] value();
}
2. 处理器Handler增加访问用户配置
Handler类中增加securityUsers
数组属性,表示可以访问此Handler的用户名称集合
public class Handler {
//表示可以访问的用户名称集合
private String[] securityUsers;
//ignore setter/getter
}
3. 前端控制器初始化过程的加载访问用户
- 在初始化处理器映射器方法
initHandlerMapping
中加载每个handler的访问用户
private void initHandlerMapping() {
if(iocMap.isEmpty()) {return;}
//遍历ioc容器中的所有controllerBean
for(Map.Entry<String,Object> entry: iocMap.entrySet()) {
// 获取ioc中当前遍历的对象的class类型
Class<?> aClass = entry.getValue().getClass();
//... ignore some codes
//访问的用户
String[] securityUsers = null;
if(aClass.isAnnotationPresent(Security.class)) {
Security annoation = aClass.getAnnotation(Security.class);
securityUsers = annoation.value();
}
//... ignore some codes
//设置handler的访问用户
handler.setSecurityUsers(securityUsers);
//... ignore some codes
}
}
4. 前端控制器接收用户请求时判断权限处理
- 在
doPost
中根据当前请求的用户参数username
判断是否拥有权限访问(checkSecurity)。如果没有权限访问则输出403 forbidden: user not has permission
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
//根据请求信息获取到相应的handler
Handler handler = getHandler(req);
//没有找到,则报404错误
if(handler == null) {
resp.getWriter().write("404 not found");
return;
}
//检查访问权限
if (!checkSecurity(req, resp, handler)) return;
//... ignore some codes
}
/**
* 检查是否拥有权限
*/
private boolean checkSecurity(HttpServletRequest req, HttpServletResponse resp, Handler handler) throws IOException {
if(handler.getSecurityUsers() != null) {
//前端的参数,访问的用户
String username = req.getParameter("username");
if(null == username || username.length() == 0) {
resp.getWriter().write("403 forbidden:user not login");
return false;
}
//将handler的用户数据跟前端传递的用户进行匹配
Optional<String> findOptional = Arrays.stream(handler.getSecurityUsers())
.filter(row -> username.equals(row))
.findAny();
if(!findOptional.isPresent()) {
resp.getWriter().write("403 forbidden: user not has permission");
return false;
}
}
return true;
}
测试
- 创建Controller的测试类以及测试方法
在类以及方法上都有标识了@Security。最终以方法配置的用户名为准,即只有xiaoming,xiaodong可以访问。如果xiaohuang访问则会报没权限的错误
在浏览器中通过
http://localhost:8080/security_demo/test?username=xiaohuang
进行测试
@Controller
@RequestMapping("/security_demo")
@Security({"xiaoming", "xiaohuang"})
public class SecurityDemoController {
/**
* 测试
* URL: /security_demo/test?username=xiaodong
* @param username
* @return
*/
@RequestMapping("/test")
@Security({"xiaoming", "xiaodong"})
public String test(String username) {
return "Login Success," + username;
}
}