高级微服务架构师工作日常记录:springMVC和springSecurity

262 阅读17分钟
原文链接: www.toutiao.com

这个例子是如何工作的

这是一个典型的基于Spring MVC的web应用程序。 它有一个默认主页所有人都可以访问。 也有三个页面,每个都有不同的访问级别。 用户只能登录与一个特定的角色为了访问这些页面。 也有一个简单的rest式服务,也是受保护的。 用户只能使用该服务时登录与适当的角色。

用户身份验证和授权信息保存在一个纯文本文件。 当一个用户试图访问一个页面,用户没有访问,它会显示登录页面。 一旦用户可以用正确的凭据登录,用户将分配适当的角色,然后用户将被授权访问该页面。

为了演示如何创建一个rest式服务,我只增加了一个RestController类和方法。 我只是想证明一点,rest式服务可以由Spring安全保护。 Spring Security作为一个传统的身份验证和授权配置组件。 合法用户登录一次,一个会话将被用于跟踪用户的身份验证和授权,直到用户注销或由于长期不活动拉开序幕。

有相当多的文件。 但是我不会在每一个人,就是至关重要的。 希望它会画一幅画,清楚地描述了Spring MVC,Spring Security和春季其他作品,在一起。 如果不是这样,下载源代码和运行一下看看。 让我们挖的。

启动类

如前所述,使用XML而不是用于指定spring配置,一切都是用Java注释。 你甚至不会看到web . xml。 为了得到这个工作,必须有一个入口点——一个服务器容器类可以识别并启动web应用程序。 这里是类:

高级微服务架构师工作日常记录:springMVC和springSecurity

这门课很容易理解。 它被称为ServletStart。 从一个类继承AbstractAnnotationConfigDispatcherServletInitializer。 当个web应用程序部署到web服务器,容器将承认这类启动类。AbstractAnnotationConfigDispatcherServletInitializer春天是一个接头类的吗DispatcherServlet类,它让我的课ServletStart春天的一个子类DispatcherServlet类。 这使我的类a类。 因此它可以用于启动web应用程序。

有三个方法,不使用中间的一个。 第一个返回一个类类型对象数组。 指定的两类RootConfig和WebConfig。 这两个类的实例提供更多的配置。 最后一个返回servlet映射路径字符串数组。 是“指定的servlet映射/”。 这是相当于标签”<servlet-mapping>“在一个web . xml文件。

配置的注释

在前一节中介绍了两类。 一个叫做RootConfig,另一个叫做WebConfig。 正如前面提到的,它们是用于提供web应用程序的配置。 让我们先从RootConfig。 这是非常简单的:

高级微服务架构师工作日常记录:springMVC和springSecurity

RootConfig类本身并不多,它没有属性和方法。 重要的是两个注释类。 注释@Configuration表示类提供了配置信息。 其他的注释@ComponentScan(...)告诉Spring框架的包中指定参数,做扫描的注射类,并创建所有对象的依赖项注入图。 诀窍是指定根包,在本例中是“org.hanbo.general.web”,它会通过所有下面的子包,所有的注射类。

是什么让一个类一个注射类? 如果您标注了一个类@Component,@Service,@Controller,@RestController等。然后这些类可以autowired的通过@Autowired到其他类。

还有另一个阶级,”WebConfig”。 这是代码:

高级微服务架构师工作日常记录:springMVC和springSecurity

这类“WebConfig”是稍微复杂。 它扩展了类称为WebMvcConfigurerAdapter。 这个基类接口的一个默认实现WebMvcConfigurer。 通过扩展它,我可以重写接口中的所有方法的一个子集。 在这种情况下,我只能实现两个方法。 一个是addResourceHandlers()。 重写这个方法让我硬编码的文件夹”/资产/ * *”(位于webapps文件夹)为基础的位置所有静态内容和网页模板。 另一种方法是viewResolver()。 这个规定,我想使用JSTL视图建设; 从位置”/ web - inf /视图/ jsp /”,所有可以使用页面模板和web应用程序的文件查找扩展”. jsp”。 在这当我们到达MVC控制器。

这个类也注释@Configuration和@ComponentScan。 我不认为注释@ComponentScan这里是必要的,因为它已经在“指定RootConfig”。 我离开这就表明,如果你添加一个@ComponentScan任何类注释@Configuration注射类的包,它会扫描。 你可以通过删除,另一个在和运行整个应用程序,看看整个事情仍能工作。

注释@EnableWebMvc允许所有的配置中定义的类WebConfig被添加到web应用程序。 这是所有关于这个类。

安全配置

将Spring security添加到这个web应用程序,带我一段时间去了解。 超级简单。 首先是添加一个类AbstractSecurityWebApplicationInitializer。 这是代码:

高级微服务架构师工作日常记录:springMVC和springSecurity

在web应用程序启动时,上面的类将注册一个springSecurityFilterChain对象。 所有通过组件扫描,我猜。 然后我们需要的是一个类,它将扩展WebSecurityConfigurerAdapter。 这是代码:

高级微服务架构师工作日常记录:springMVC和springSecurity

这类“SecurityConfig”注释@Configuration。 这意味着类提供配置。 这也是注释@EnableWebSecurity,这表明Spring security将支持这个应用程序,和安全配置将会发现在这类(因为它是注释@EnableWebSecurity)。

注释@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)是一个有趣的人。 我添加了它,这样我可以使用角色授权基于MVC的行动方法。 的控制器,所有的操作方法@PreAuthorize("hasRole('ROLE_...')")或@PreAuthorize("hasAnyRoles('ROLE_...', 'ROLE_...', ...)")。 当用户访问这些操作方法,除非用户登录和有必要的角色,用户将得到403。

现在,让我们仔细分析这个类中定义的方法。 第一个,configure(HttpSecurity http)定义了安全是如何工作的,当用户与web应用程序进行交互。方法调用链基本如下:

  • 任何子web应用程序的url将被允许匿名访问。 这样做的原因是,我只想锁定特定页面安全地访问。

  • 登录页面将被路由到”/登录”。 ,这将是一个表单,用户可以输入凭证获取。

  • 表单将包含两个字段,“username”和“password“这将会提取并传递给身份验证管理器。

  • 当用户登录时,他们看到的第一件事就是页面”/索引”。

  • 当用户身份验证失败,将显示页面,将“/ accessDenied”。

  • 我使用SavedRequestAwareAuthenticationSuccessHandler类处理场景,登录成功后,页面将跳转到页面,用户试图访问。

  • 注销请求处理url”/注销”。 注意,清理身份验证和授权饼干是由Spring Security,没有需要额外的代码。 然而,为了显示一个注销页面,我打电话给法”logoutSuccess()”。

第二种方法是设置身份验证提供者。 这是一个非常重要的配置。 一个定制的身份验证提供者允许您实现您自己的方式使用输入用户名和密码进行身份验证。 一个典型的场景将会把用户名和密码,找到用户数据库,比较散列密码散列密码数据库。 如果他们匹配,那么加载用户的角色和附加给用户。 之后,当用户访问一个页面时,与用户关联的角色将被用来确定授权。

最后一个方法的类定义了一个bean。 有一些问题与安全配置。 我认为这可能是一个错误在Spring security我用的版本。 这迫使我定义一个AccessDeniedHandler所以每当发生拒绝访问,它将重定向到一个拒绝访问页面。 在我们开始我的定义AccessDeniedHandler,被称为SampleAccessDeniedHandler,我将首先告诉你定制的身份验证提供者。

自定义身份验证提供者

对于这个示例web应用程序,我已经硬编码的四个不同的用户在一个文本文件,名为“UserInfo.txt“在资源文件夹。 我的身份验证提供者(称为UserAuthenticationService)将加载文件,使用反序列化到一个Java数组。 然后用户提供用户名和密码进行身份验证。 在通过身份验证之后,所有的用户角色与用户将被添加到用户的身份验证令牌。 这是代码:

高级微服务架构师工作日常记录:springMVC和springSecurity

高级微服务架构师工作日常记录:springMVC和springSecurity

这个类是非常直截了当的。 第一个方法authenticate()验证用户的工作。 首先,它检查用户名和密码,确保他们不是null还是空的。 然后被传递给一个用户名和密码LoginUserService对象。 这LoginUserService从文本文件加载所有用户。 反序列化的文本string作为一个JSon对象的列表UserModel对象。 将用于查找用户名UserModel对象匹配,输入密码散列,相比之下,的散列密码UserModel对象。 如果匹配,那么UserModel对象将被返回authenticate()。 在authenticate(),如果有一个有效的UserModel对象,然后它会检查确保用户仍然是活跃的,如果用户,那么相关的所有角色将被添加到用户的身份验证令牌和回报。 如果所有检查失败,null返回,而不是一个有效的身份验证令牌。

的方法将用户角色添加到身份验证令牌的方法createLoginUserAuthority:

高级微服务架构师工作日常记录:springMVC和springSecurity

什么这个方法是,如果用户管理员,它所有的角色(包括员工、用户和客人)。 如果用户的角色人员,那么用户也将角色用户和客人。 如果用户的角色用户,那么用户将用户和角色的客人。 如果用户的角色的客人,那么用户只会客人的角色。 如果没有角色与用户相关联,用户的角色列表将被清除。

在我的第二种方法UserAuthenticationService被称为supports。 这是代码:

高级微服务架构师工作日常记录:springMVC和springSecurity

它所做的是告诉web应用程序类型的身份验证令牌UsernamePasswordAuthenticationToken。 这些都是有关于这个UserAuthenticationService。

拒绝访问处理程序

我需要一个拒绝访问处理程序的原因是我想拦截拒绝访问异常,那么我可以重定向响应定制拒绝访问页面。 我必须这么做我相信的原因是我配置授权互动或这是一个错误的版本的Spring Security我使用。 我敢打赌我配置错误的春天授权。 如果你有兴趣,试着解决这个问题。 即使这是一个问题,它提出了一个学习的机会。 在这一节中,我将向您展示这个拒绝访问处理程序是如何工作的。 这是代码:

高级微服务架构师工作日常记录:springMVC和springSecurity

正如您可以看到的,实现的SampleAccessDeniedHandler只有一个方法来覆盖。 它被称为handle()。 该方法接受一个HttpServletRequest对象,HttpServletResponse对象,并拒绝访问异常对象。 基本上,这个类的对象将拦截拒绝访问异常与原来的请求和响应对象,最终将返回给用户。

不管原始请求,例外,该方法handle()将首先获得上下文路径,这是web应用程序的基URL。 然后我添加子URL”/ accessDenied”。 和使用,随着重定向URL。 最后,我将重定向URL设置为响应。 这就是它。 当调用该方法时,它将重定向到该URL。

我喜欢封面的最后一件事就是控制器来处理用户身份验证和授权。 类被称为UsersController,这是代码:

高级微服务架构师工作日常记录:springMVC和springSecurity

在这个类中,有三种方法,每处理一个URL。 他们可以匿名访问。 第一个叫做login()。 它将用户请求路由到登录页面。 第二个loginError()。 这一个用户请求路由到登录错误页面。 最后一个logoutPage(),这将显示注销页面在用户注销。

这些方法都简单的操作方法(。 网络术语)。 他们都返回的对象类型ModelAndView。 的对象ModelAndView有一些有用的方法,可以调用:

  • setViewName()这将jsp模板,将用于显示

  • addObject(),这将向模型中添加视图数据的视图模板可以使用它来创建最终的视图

更多关于Spring MVC将在下一节中介绍。 登录页面的页面显示是这样的:

高级微服务架构师工作日常记录:springMVC和springSecurity

语法是不同于普通的HTML。 我正在使用JSTL,这样我就可以把页面分成组件和布局。 正如您可以看到的,在上面的JSP代码,有两个输入字段:

  • 一个被称为“username”

  • 另一种被称为“password”

这两个字段的使用SecurityConfig识别的用户凭证(见configure()方法)。 记住的一个重要的事情是,当提交登录表单,提交”/登录“url,使用的http方法是“post”。 另一件重要的事情,我使用csrf令牌提供额外的安全验证。 这是你看到的地方name="${_csrf.parameterName}"。 你可以禁用它,如果你想。 但是对于基于MVC的web应用程序,使用CSRF额外安全措施是好的做法。

Spring MVC控制器

与Spring Security演示Spring MVC,我创建了两个控制器。 一个叫做TestController,处理基于MVC的web页面。 另一种是电话TestApiController,处理RESTFul请求。 这里的代码TestController:

高级微服务架构师工作日常记录:springMVC和springSecurity

比较我们经历的所有配置代码,这一点TestController类是很容易理解。 类注释@Controller,这表明类TestController是一个MVC控制器。 在这个类中,有五个public方法,前两个是代表默认主页(访问通过“/”或“/索引”),他们都是相同的。 其他三种方法每个表示一个页面,用户与一个特定的角色可以访问:

  • testPage1():只有用户与管理员角色可以访问这个页面

  • testPage2():只有用户和员工角色可以访问这个页面

  • testPage3():只有用户与用户角色可以访问这个页面

所有这些方法都容易理解。 所有这些方法没有参数。 都是注释@RequestMapping。 这个注释设置子url(通过参数value对上下文路径)。 它还允许开发人员定义的HTTP方法(通过参数method),这种方法可以处理。 有两个参数,可以使用该注释进一步指定该方法如何处理http请求。 我不打算覆盖了。

这三种方法的注释@PreAuthorize。 这个注释指定授权访问这个方法是必要的。 这是基于角色的授权机制。 传入的参数可以是这样的:

高级微服务架构师工作日常记录:springMVC和springSecurity

基本上,这个值是一个表达式。 只有当身份验证令牌的角色<role name>。 一个角色名是这样的:ROLE_SITE_ADMIN;或ROLE_SITE_STAFF;等等。记住的一个重要的事情是,如果你使用基于角色的授权、角色名称和前缀开始”ROLE_”。 如果没有这个前缀,基于角色的授权无法工作。 试一试。 您可以删除”的前缀ROLE_“从所有的事件”ROLE_SITE_STAFF”。 然后登录一个网站工作人员和尝试访问testPage2。 你会得到一个“Access Denied”错误。 通过观察这些@PreAuthorize注释,您可以猜出用户角色需要访问这些页面。

所有方法做类似的事情,返回ModelAndView对象视图模板,并在模型中一些值,这样他们就可以显示在web页面中。 接下来,我将介绍一些在RESTFul api。

RESTFul API演示

为了简单起见,我创建了一个简单的rest控制器,证明了这是可以做到的,这是非常简单的。 类被称为TestApiController。 这是代码:

高级微服务架构师工作日常记录:springMVC和springSecurity

这门课几乎是一样的MVC控制器TestController。 这是注释,@RestController相反,这意味着控制器类是rest式服务为MVC web页面导航。 在这类只有一个方法。 它被称为getCarModel()。 这需要在一个string参数被称为“make”和“model”,做一些计算,然后返回一个对象的,模型,和价格的汽车。 当然,这是一个假动作方法(a。 净,再一次)。 是硬编码返回一个特定的模型。 有趣的是,汽车模型序列化为JSON对象并返回。 我喜欢用ResponseEntity因为我可以设置http状态代码(代码200,200,404,401或500)响应对象。 但我意识到,有时候它是多余的,有时候,我只需要返回实际的对象,它可以自动序列化为JSON对象返回。

该方法注释@PreAuthorize,@RequestMapping,@ResponseBody。 注释@ResponseBody执行的响应被序列化为JSON字符串。 所以从技术上说,我不需要包装对象内ResponseEntity对象。 我只是为了好玩,并展示,如果你需要设置http状态代码作为响应的一部分,ResponseEntity是如何完成它。

这两个参数注释@RequestParam。 这个注释表明这两个参数的值来自url的查询字符串,例如:

高级微服务架构师工作日常记录:springMVC和springSecurity

查询字符串问号之后的一切。 如果您使用上面的网址,如果你登录网站用户角色,那么你应该看到以下反应:

高级微服务架构师工作日常记录:springMVC和springSecurity

接下来,我将讨论如何测试这个应用程序。

如何构建和测试

如何构建

下载示例应用程序的来源之后,做的第一件事是进入js文件夹下的所有资产,和重命名* sj文件恢复* . js。 项目可以通过它建立了一个运行。 建立gradle,命令是:

高级微服务架构师工作日常记录:springMVC和springSecurity

执行它,运行以下命令:

高级微服务架构师工作日常记录:springMVC和springSecurity

您还可以使用它来创建Eclipse项目:

高级微服务架构师工作日常记录:springMVC和springSecurity

eclipse项目和类路径文件生成后,您可以将它们导入eclipse。 请注意,所有这些都是用Java 1.8。

如何测试

当您使用命令gradle jettyRun,如果一切顺利,您将看到在命令行控制台没有错误。 这是一个示例屏幕截图:

高级微服务架构师工作日常记录:springMVC和springSecurity

访问默认索引页面,网址是:http://localhost:8080 / SampleSpring4指数:http://localhost:8080 / SampleSpring4 /。 当您导航到这个页面,您将看到以下几点:

高级微服务架构师工作日常记录:springMVC和springSecurity

进入测试页面# 1,只有访问网站管理员帐户。 用户与网站管理员角色”testadmin”。 密码是“123test321”(没有双引号)。 为了测试这个用户角色,导航到:http://localhost:8080 / SampleSpring4 /安全/ testPage1。 您首先会看到登录页面。 输入正确的用户名和密码,您将会看到以下几点:

高级微服务架构师工作日常记录:springMVC和springSecurity

看导航栏,有三个测试页面可查看。 用户与网站管理员角色也有网站工作人员,和网站用户角色。 让用户有完全访问这个web应用程序能够提供的所有页面。 你应该注销之前你试着下一个用户。 在右上角,有一个下拉导航菜单“Account”。 点击它,你就会看到“退出”选项。 点击它会记录你的用户。让我们尝试登录使用用户与网站工作人员的作用。 这是超级用户角色和特权略低于admin角色。 用户是“teststaff”和密码仍然是“123test321”。 导航到URL:http://localhost:8080 / SampleSpring4 /安全/ testPage2。 然后登录”teststaff”,您将看到以下截图:

高级微服务架构师工作日常记录:springMVC和springSecurity

现在你可以看到这个用户,没有可用的测试页面# 1。 如果你试图通过url导航到测试页面# 1:http://localhost:8080 / SampleSpring4 /安全/ testPage1。 你将收到一个403错误页面。

最后,您可以测试该网站用户帐户。 先退出,然后导航到:http://localhost:8080 / SampleSpring4 /安全/ testPage3。 与用户登录”testuser1“和密码”123test321”。 您将看到以下截图:

高级微服务架构师工作日常记录:springMVC和springSecurity

现在,尝试基于rest的API服务。 它与角色访问网站的用户。 导航到URL:http://localhost:8080 / SampleSpring4 /安全/ api / getCarModel ?做= subaru&model = +

高级微服务架构师工作日常记录:springMVC和springSecurity

最后,我们可以试着用一个无效的用户。 用户”testuser2“可以用相同的登录密码。 然而,这个用户是禁用的。 所以你不能登录。

高级微服务架构师工作日常记录:springMVC和springSecurity

推荐一个交流学习群:478030634 里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多:

高级微服务架构师工作日常记录:springMVC和springSecurity