上一篇文章: Spring Security 4 Hello World 基于注解 和 XML 例子 下一篇:Spring Security 4 退出 示例
原文地址:http://websystique.com/spring-security/spring-security-4-custom-login-form-annotation-example/
【已翻译文章,点击分类里面的spring security 4查看。】
【 翻译by 明明如月 QQ 605283073】
本文演示Spring Security 4整合Spring MVC web应用的自定义登录表单
在 Spring Security 4 Hello World Annotation+xml 例子中,我们已经看到了如果我们自己不指定登录表单,Spring Security 提供的默认的登录表单。在本文,我们将创建自己的登录表单。
基本思想是,Security Configuration 类中 增加调用formLogin() 的loginPage(URL) 方法
如下:
.and().formLogin().loginPage("/login")
然后,在Spring MVC Controller 中映射“/login” url对应自己写的登录view ,返回登录界面。
这样,如果尝试登录,自定的登录界面将会被显示。其他的登录方法也一样。
下面是针对此方案提供的一个完整的例子
---------------------------------------------------------------------
所用到的技术或者软件:
让我们开始吧。。。
---------------------------------------------------------------------------
下面是最终的项目目录结构
现在我们添加上面结构提到的文件和具体内容。
4.0.0
com.websystique.springsecurity
SpringSecurityCusotmLoginFormAnnotationExample
1.0.0
war
SpringSecurityCusotmLoginFormAnnotationExample
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
1.7
org.apache.maven.plugins
maven-war-plugin
2.4
src/main/webapp
SpringSecurityCusotmLoginFormAnnotationExample
false
SpringSecurityCusotmLoginFormAnnotationExample
想添加spring security到你的应用中第一步是创建 Spring Security Java Configuration(配置).
这个配置创建一个叫 springSecurityFilterChain 的servlet 过滤器,来对我们应用中的所有的安全相关事项负责。
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");
}
@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().loginPage("/login")
.usernameParameter("ssoId").passwordParameter("password")
.and().csrf()
.and().exceptionHandling().accessDeniedPage("/Access_Denied");
}
}
这个类和 上一篇文章 中几乎是一样的。唯一的区别是:
.and().formLogin().loginPage("/login")
.usernameParameter("ssoId").passwordParameter("password")
.and().csrf()
这段代码指定url为"/login"作为自定义的登录界面,并用 ssoId 作为用户名和password 作为密码参数。
我们也添加了一个可选方法 csrf()的调用,默认在Spring Security 4中该方法是激活的。
然而此调用是需要的,如果你先关闭csrf保护可以通过调用csrf().disable() 来实现,虽然这不是一个好主意。
上面的安全配置对应的XML 形式如下:
下面初始化类注册 springSecurityFilter
(在第3步中创建的)。
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
/*
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 = "/Access_Denied", method = RequestMethod.GET)
public String accessDeniedPage(ModelMap model) {
model.addAttribute("user", getPrincipal());
return "accessDenied";
}
@RequestMapping(value = "/login", method = RequestMethod.GET)
public String loginPage() {
return "login";
}
@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 "redirect:/login?logout";
}
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;
}
}
和上一篇文章的区别在于添加了 loginPage方法来处理“/login”请求,改变logout方法,退出后重定向到登录界面
@RequestMapping(value = "/login", method = RequestMethod.GET)
public String loginPage() {
return "login";
}
@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 "redirect:/login?logout";
}
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.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.JstlView;
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.websystique.springsecurity")
public class HelloWorldConfiguration extends WebMvcConfigurerAdapter{
@Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
/*
* Configure ResourceHandlers to serve static resources like CSS/ Javascript etc...
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**").addResourceLocations("/static/");
}
}
和之前的不同是拓展自 WebMvcConfigurerAdapter类并覆盖了addResourceHandlers 方法来处理静态资源,使其可以在view中使用。
---------译者增加 start---明明如月-------- 以上配置对应的xml配置如下:
/static/**" location="/static/" />
---------译者增加end---明明如月--------
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[] { "/" };
}
}
此类和上一篇文章也是一样的。
第 8步: 添加Views(视图)
login.jsp
此视图为登录面板增加了css
<%@ 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"%>
Login page
Invalid username and password.
You have been logged out successfully.
注意:和 CSRF 相关的是
< input type = "hidden" name = "${_csrf.parameterName}" value = "${_csrf.token}" /> strong > |
---|
这一行的目的是防止CSRF攻击。正如你所见jsp中CSRF参数使用EL表达式获取的。因此需要允许el表达式:
需要在jsp头添加如下一行:
<%@ page isELIgnored="false"%>
welcome.jsp
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
Welcome page
Greeting : ${greeting}
This is a welcome page.
admin.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"%>
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
例子中所需的css文件
app.css
html{
background-color:#2F2F2F;
}
body, #mainWrapper {
height: 100%;
background-image: -webkit-gradient(
linear,
right bottom,
right top,
color-stop(0, #EDEDED),
color-stop(0.08, #EAEAEA),
color-stop(1, #2F2F2F),
color-stop(1, #AAAAAA)
);
background-image: -o-linear-gradient(top, #EDEDED 0%, #EAEAEA 8%, #2F2F2F 100%, #AAAAAA 100%);
background-image: -moz-linear-gradient(top, #EDEDED 0%, #EAEAEA 8%, #2F2F2F 100%, #AAAAAA 100%);
background-image: -webkit-linear-gradient(top, #EDEDED 0%, #EAEAEA 8%, #2F2F2F 100%, #AAAAAA 100%);
background-image: -ms-linear-gradient(top, #EDEDED 0%, #EAEAEA 8%, #2F2F2F 100%, #AAAAAA 100%);
background-image: linear-gradient(to top, #EDEDED 0%, #EAEAEA 8%, #2F2F2F 100%, #AAAAAA 100%);
}
body, #mainWrapper, .form-control{
font-size:12px!important;
}
#mainWrapper {
height: 100vh;
padding-left:10px;
padding-right:10px;
padding-bottom:10px;
}
#authHeaderWrapper{
clear:both;
width: 100%;
height:3%;
padding-top:5px;
padding-bottom:5px;
}
.login-container {
margin-top: 100px;
background-color: floralwhite;
width: 40%;
left: 30%;
position: absolute;
}
.login-card {
width: 80%;
margin: auto;
}
.login-form {
padding: 10%;
}
现在构建 war 包(通过eclipse或者myeclipse)或者通过maven 命令行( mvn clean install
). 在一个 Servlet 3.0 容器中发布本应用. 在这里我使用的是tomcat, 我将 war 文件放到 tomcat webapps 文件夹然后点击
tomcat安装目录的bin文件夹下的 start.bat
.
启动应用 打开浏览器 在地址栏输入 localhost:8080/SpringSecurityHelloWorldAnnotationExample/并回车
现在试着访问admin界面
localhost:8080/SpringSecurityCusotmLoginFormAnnotationExample/admin
将会触发登录界面
输入一个 USER 角色的账户
提交后 将显示 权限拒绝界面
点击 退出 并尝试访问admin页面
输入错误的密码
输入正确的admin账户
现在通过localhost:8080/SpringSecurityCusotmLoginFormAnnotationExample/db 访问db页面
将显示 权限拒绝界面
退出
下一篇文章实现自定义退出方法,并对浏览器后退按钮也进行了处理。
源码下载地址:http://websystique.com/?smd_process_download=1&download_id=1363