下一篇:
原文:http://websystique.com/spring-security/spring-security-4-hello-world-annotation-xml-example/
【已翻译文章,点击分类里面的spring security 4进行查看】
【翻译by 明明如月 QQ 605283073】
本教程演示Spring MVC web项目中Spring Security 4的用法。通过url对访问进行验证。
我们将通过一个经典的hello world例子来学习Spring Security 4 的基本用法。
本文使用基于Servlet3.0容器的Spring注解(因此没有web.xml文件)。同样也会给出基于Security 配置的xml配置。
所用到的技术和软件:
让我们开始吧...
现在让我为你展示上面目录结构里面的内容和每个的详细介绍。
4.0.0
com.websystique.springsecurity
SpringSecurityHelloWorldAnnotationExample
1.0.0
war
SpringSecurityHelloWorldAnnotationExample
4.1.6.RELEASE
4.0.1.RELEASE
org.springframework
spring-core
${springframework.version}
org.springframework
spring-web
${springframework.version}
org.springframework
spring-webmvc
${springframework.version}
org.springframework.security
spring-security-web
${springsecurity.version}
org.springframework.security
spring-security-config
${springsecurity.version}
javax.servlet
javax.servlet-api
3.1.0
javax.servlet.jsp
javax.servlet.jsp-api
2.3.1
javax.servlet
jstl
1.2
org.apache.maven.plugins
maven-compiler-plugin
3.2
1.7
org.apache.maven.plugins
maven-war-plugin
2.4
src/main/webapp
SpringSecurityHelloWorldAnnotationExample
false
SpringSecurityHelloWorldAnnotationExample
首先需要注意的是maven-war-plugin
的声明。鉴于我们使用纯注解,甚至都没用web.xml。因此我们需配置此插件防止maven创建war包失败。
我们使用的是Spring 和 Spring Security(在本文发表时)最新版本。与此同时,由于我们将使用servlet api和jstl在我们界面中,我们也添加了JSP/Servlet/Jstl的依赖。
一般来说,容器也许已经包含了这些库,所以我们在pom.xml文件中,可以设置他们的scope 为provided。
添加spring security到我们应用中第一步是要创建Spring Security Java 配置类。
这个配置创建一个叫springSecurityFilterChain的Servlet过滤器,来对我们应用中所有的安全相关的事项(保护应用的所有url,验证用户名密码,表单重定向等)负责。
com.websystique.springsecurity.configuration.SecurityConfiguration
package com.websystique.springsecurity.configuration;
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 SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("bill").password("abc123").roles("USER");
auth.inMemoryAuthentication().withUser("admin").password("root123").roles("ADMIN");
auth.inMemoryAuthentication().withUser("dba").password("root123").roles("ADMIN","DBA");//dba have two roles.
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/", "/home").permitAll()
.antMatchers("/admin/**").access("hasRole('ADMIN')")
.antMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')")
.and().formLogin()
.and().exceptionHandling().accessDeniedPage("/Access_Denied");
}
}
上面这个类的configureGlobalSecurity方法为 AuthenticationManagerBuilder配置用户授权和角色信息 。
此AuthenticationManagerBuilder (权限管理器创建器)创建负责所有权限请求的AuthenticationManager(权限管理器)。
注意:在上面例子中,我们使用的是 基于内存的权限认证,当然你也可以自由选择JDBC,LDAP或者基于其他技术的权限认证。
重写
Configure
方法,来配置
HttpSecurity
来配置基于特定http请求的安全认证。
它默认是实用所有请求的,但是也可以通过requestMatcher(RequestMatcher)/antMathchers 或者其他类似的方法进行限定。
在上述配置中,我们可以看到‘/’ & ‘/home’这种Url配置是不安全的,任何人都可以访问。
只有具有ADMIN权限的用户才可以访问符合‘/admin/**’的url。只能够同时具有ADMIN 和 DBA权限的人才可以访问符合‘/db/**’ 的Url 。
formLogin
方法提供了基于表单的权限验证,将会产生一个默认的对用户的表单请求。
你也可以自定义登录表单。在接下来的文章里面,你可以看到类似的例子。
我们也会使用
exceptionHandling().accessDeniedPage()
,在本例中它将获取所有的403(http访问拒绝)异常然后显示我们的用户定义的HTTP403页面(虽然也没有太大益处)。
上面的安全配置 XML 配置形式如下:
下面是定制初始化war包中的springSecurityFilter(第三步中的)注册类。
com.websystique.springsecurity.configuration.SecurityWebApplicationInitializer
package com.websystique.springsecurity.configuration;
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer {
}
上面对应的xml配置形式为:
springSecurityFilterChain
org.springframework.web.filter.DelegatingFilterProxy
springSecurityFilterChain
/*
com.websystique.springsecurity.controller.HelloWorldController
package com.websystique.springsecurity.controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
public class HelloWorldController {
@RequestMapping(value = { "/", "/home" }, method = RequestMethod.GET)
public String homePage(ModelMap model) {
model.addAttribute("greeting", "Hi, Welcome to mysite. ");
return "welcome";
}
@RequestMapping(value = "/admin", method = RequestMethod.GET)
public String adminPage(ModelMap model) {
model.addAttribute("user", getPrincipal());
return "admin";
}
@RequestMapping(value = "/db", method = RequestMethod.GET)
public String dbaPage(ModelMap model) {
model.addAttribute("user", getPrincipal());
return "dba";
}
@RequestMapping(value="/logout", method = RequestMethod.GET)
public String logoutPage (HttpServletRequest request, HttpServletResponse response) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null){
new SecurityContextLogoutHandler().logout(request, response, auth);
}
return "welcome";
}
@RequestMapping(value = "/Access_Denied", method = RequestMethod.GET)
public String accessDeniedPage(ModelMap model) {
model.addAttribute("user", getPrincipal());
return "accessDenied";
}
private String getPrincipal(){
String userName = null;
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (principal instanceof UserDetails) {
userName = ((UserDetails)principal).getUsername();
} else {
userName = principal.toString();
}
return userName;
}
}
SecurityContext中记录的登录的用户。
logoutPage 方法简单调用 SecurityContextLogoutHandler().logout(request, response, auth)方法
来处理退出操作。
它很巧妙而且将你从不容易管理的jsp页面退出逻辑中解放出来。
你也许注意到上面没有出现 /login’,因为Spring Security默认会产生和处理。
com.websystique.springsecurity.configuration.HelloWorldConfiguration
package com.websystique.springsecurity.configuration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.websystique.springsecurity")
public class HelloWorldConfiguration {
@Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
}
package com.websystique.springsecurity.configuration;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
public class SpringMvcInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class>[] getRootConfigClasses() {
return new Class[] { HelloWorldConfiguration.class };
}
@Override
protected Class>[] getServletConfigClasses() {
return null;
}
@Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
注意上面的初始化器继承自
AbstractAnnotationConfigDispatcherServletInitializer
,它是所有WebApplicationInitializer
实现的基类.
在Servlet 3.0 环境下,通过实现WebApplicationInitializer 来配置ServletContext 。这意味着我们将不使用web.xml而且将在支持servlet3.0容器下发布此应用。
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
HelloWorld page
Greeting : ${greeting}
This is a welcome page.
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
HelloWorld Admin page
Dear ${user}, Welcome to Admin Page.
">Logout
dba.jsp
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
DBA page
Dear ${user}, Welcome to DBA Page.
">Logout
accessDenied.jsp
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
AccessDenied page
Dear ${user}, You are not authorized to access this page
">Logout
正如第7步提到的, 在我们应用中没有用到web.xml作为ServletContext 来启动程序.
现在构建 war 包(通过eclipse或者myeclipse)或者通过maven 命令行( mvn clean install
). 在一个 Servlet 3.0 容器中发布本应用. 在这里我使用的是tomcat, 我将 war 文件放到 tomcat webapps 文件夹然后点击
tomcat安装目录的bin文件夹下的start.bat
.
启动应用
打开浏览器 在地址栏输入 localhost:8080/SpringSecurityHelloWorldAnnotationExample/并回车
通过 localhost:8080/SpringSecurityHelloWorldAnnotationExample/admin 来访问admin 页面, 你将会被引导到登录页面.
输入一个USER角色的账户
提交表单, 你将看到AccessDenied(访问拒绝)页面
退出然后再次访问admin页面
输入错误的password(密码)