概述
展示Spring Security中基于角色的登录。也就是说,根据其角色登录以后重定向到不同的url。
一般来说,我们需要自定义一个SuccessHandler 来根据用户角色处理登录用户的重定向到对应的url。
这个功能在Spring Security 里面已经提供了。
SimpleUrlAuthenticationSuccessHandler 含有常用的successhandler的常用逻辑。
我们仅需要拓展它,实现我们自己的逻辑即可。
版本
- Spring 4.1.6.RELEASE
- Spring Security 4.0.1.RELEASE
- Maven 3
- JDK 1.7
配置实现
项目结构
pom 依赖
主要依赖的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
| <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <springframework.version>4.1.6.RELEASE</springframework.version> <springsecurity.version>4.0.1.RELEASE</springsecurity.version> </properties> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${springframework.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>${springframework.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${springframework.version}</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-web</artifactId> <version>${springsecurity.version}</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-config</artifactId> <version>${springsecurity.version}</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> </dependency> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>javax.servlet.jsp-api</artifactId> <version>2.3.1</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> </dependencies> <build> <finalName>sprsec</finalName> <plugins> <plugin> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-maven-plugin</artifactId> <version>9.4.7.v20170914</version> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-war-plugin</artifactId> <version>2.4</version> <configuration> <failOnMissingWebXml>false</failOnMissingWebXml> </configuration> </plugin> </plugins> </build>
|
java文件
Servlet初始化器类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 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[] { "/" }; }
}
|
这里的HelloWorldConfiguration
就是SpringMVC的配置初始化类。
AbstractAnnotationConfigDispatcherServletInitializer
是继承于WebApplicationInitializer
可能在web容器启动的时候被加载。
SpringMVC 配置类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| @Configuration @EnableWebMvc @ComponentScan(basePackages = "com.example.sprsec") public class HelloWorldConfiguration extends WebMvcConfigurerAdapter {
@Bean public ViewResolver viewResolver() { InternalResourceViewResolver viewResolver = new InternalResourceViewResolver(); viewResolver.setViewClass(JstlView.class); viewResolver.setPrefix("/WEB-INF/"); viewResolver.setSuffix(".jsp"); return viewResolver; }
@Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/css/**").addResourceLocations("/css/"); registry.addResourceHandler("/js/**").addResourceLocations("/js/"); } }
|
这个类的作用相当于把原来xml的配置变成了java代码中写的。
等价于下面的xml:
1 2 3 4 5 6 7 8 9 10 11 12
| <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix"> <value>/WEB-INF/pages/</value> </property> <property name="suffix"> <value>.jsp</value> </property> </bean> <context:component-scan base-package="com.example.sprsec" /> <mvc:resources mapping="/css/**" location="/css/" /> <mvc:resources mapping="/js/**" location="/js/" />
|
初始化 Spring Security 的类
1 2 3 4
| public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer {
}
|
这个类的作类,类似在web.xml中增加Filter。由于这个项目是servlet3.1,没有web.xml,都是用java类来配置的。
详细介绍见Spring Security 4官方文档中文翻译与源码解读
原理就是用了spring-web-4.xx.xx.jar的中SPI机制。
等同于下面配置:
1 2 3 4 5 6 7 8 9
| <filter> <filter-name>springSecurityFilterChain</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>springSecurityFilterChain</filter-name> <url-pattern>
|
Spring Security 配置类
该类主要用来配置权限与url的对应关系,及验证方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| @Configuration @EnableWebSecurity public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired CustomSuccessHandler customSuccessHandler;
@Autowired public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication().withUser("user").password("123123").roles("USER"); auth.inMemoryAuthentication().withUser("admin").password("123123").roles("ADMIN"); auth.inMemoryAuthentication().withUser("dba").password("123123").roles("ADMIN", "DBA"); }
@Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/", "/home").access("hasRole('USER')") .antMatchers("/admin/**").access("hasRole('ADMIN')") .antMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')") .and().formLogin().loginPage("/login") .successHandler(customSuccessHandler) .usernameParameter("ssoId") .passwordParameter("password") .and().csrf().and().exceptionHandling() .accessDeniedPage("/Access_Denied"); }
}
|
等价于下面的xml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <http auto-config="true" use-expressions="true"> <intercept-url pattern="/home/**" access="hasRole('ADMIN')" /> <intercept-url pattern="/db/**" access="hasRole('ADMIN') and hasRole('DBA')" /> <intercept-url pattern="/**" access="hasRole('USER')" /> <access-denied-handler error-page="/Access_Denied" /> <form-login login-page="/login" default-target-url="/index" authentication-failure-url="/login?error" username-parameter="ssoId" password-parameter="password" /> <csrf/> </http>
|
验证后的处理类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| @Component public class CustomSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
@Override protected void handle(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { String targetUrl = determineTargetUrl(authentication); if (response.isCommitted()) { System.out.println("Can't redirect"); return; } redirectStrategy.sendRedirect(request, response, targetUrl); }
private String determineTargetUrl(Authentication authentication) { String url = ""; Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities(); List<String> roles = new ArrayList<String>(); for (GrantedAuthority a : authorities) { roles.add(a.getAuthority()); } if (roles.contains("ROLE_DBA")) { url = "/db"; } else if (roles.contains("ROLE_ADMIN")) { url = "/admin"; } else if (roles.contains("ROLE_USER")) { url = "/home"; } else { url = "/accessDenied"; } return url; }
}
|
控制器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| @Controller public class MainController {
@RequestMapping(value = { "/", "/home" }, method = RequestMethod.GET) public String homePage(ModelMap model) { model.addAttribute("user", getPrincipal()); 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; } }
|
jsp文件
- login.jsp 登录页面,有登录的form表单
- index.jsp 普通用户的欢迎页面
- admin.jsp 管理员的欢迎页面
- dba.jsp dab的欢迎页面
- welcome.jsp 普通用户的欢迎页面
- accessDenied.jsp 验证失败的页面
参考