Spring MVC + Security 4 初体验(Java配置版)
2017-03-21 14:14
337 查看
这篇文章同样是使用的Java配置,而非XML配置,如果你对于Java配置的Spring MVC开发还不太熟悉,可以先看我这篇文章。
转自:http://xueliang.org/article/detail/20170302232815082
复制
复制
复制
进入
这里向
接着,对登录表单进行配置。通过
这里我使用了RESTful,故不需要配置服务端的转发,而是配置了另外两处:
配置完登录相关信息之后,接着配置和登出有关的信息。和配置登录表单提交地址类似,这里需要配置登出请求提交地址,这里调用
最后,配置对异常的处理
配置完这些,看
另外,由于不能在数据库中保存明文密码,这里对密码进行
复制
复制
复制
转自:http://xueliang.org/article/detail/20170302232815082
Authority
创建一个Authority,实现自
org.springframework.security.core.GrantedAuthority类,
getAuthority方法只返回一个表示权限名称的字符串,如
AUTH_USER、
AUTH_ADMIN、
AUTH_DBA等。
复制
public class Authority implements GrantedAuthority { private static final long serialVersionUID = 1L; private String authority; public Authority() { } public Authority(String authority) { this.setAuthority(authority); } @Override public String getAuthority() { return this.authority; } public void setAuthority(String authority) { this.authority = authority; } }
User
User类实现自
org.springframework.security.core.userdetails.UserDetails接口,包含一组权限的集合
authorities。
复制
public class User implements UserDetails { private static final long serialVersionUID = 1L; private String username; private String password; private List<Authority> authorities; @Override public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } @Override public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } @Override public Collection<? extends GrantedAuthority> getAuthorities() { return this.authorities; } public void setAuthorities(List<Authority> authorities) { this.authorities = authorities; } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return true; } }
UserDetailsService
MyUserDetailsService实现了
org.springframework.security.core.userdetails.UserDetailsService的
loadUserByUsername方法,该方法根据用户名查询符合条件的用户,若没有找到符合条件的用户,必须抛出
UsernameNotFoundException异常,而不能返回空。这里可以调用 DAO 层,从数据库查询用户,我为了简单,直接将用户临时放到一个常量内,模拟从数据库查询用户。
复制
@Service public class MyUserDetailsService implements UserDetailsService { @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { List<User> userList = Constants.userList; for (int i = 0, len = userList.size(); i < len; i++) { User user = userList.get(i); if (user.getUsername().equals(username)) { return user; } } throw new UsernameNotFoundException("用户不存在!"); } }
SecurityConfig
SecurityConfig类继承
org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter,
WebSecurityConfigurerAdapter提供了一些默认的配置,方便创建一个实例。
进入
configure方法中,首先允许任何情况下的对
csrfTokenApi的请求,该 API 返回一个
csrfToken,默认情况下除
GET、
HEAD、
TRACE和
OPTIONS外,所有请求都必须经过 CSRF 认证。接下来对不同的API请求设置不同的权限,并且确保所有对
/api/下的请求都经过了认证。
这里向
access方法传递的表达式中的权限名称,对应上面提到的
Authority类中
getAuthority返回的字符串的值,详细的表达式介绍,请移步至这里。
接着,对登录表单进行配置。通过
loginProcessingUrl配置表单提交地址,这个地址对应的API不需要自己写,Spring Security 会自动拦截提交到此地址请求,将其视为登录请求。如果希望登录成功后通过服务器转发到其他页面,可以调用
successForwardUrl(String forwardUrl)方法指定跳转的地址,对应地,指定失败后跳转地址的方法是
failureForwardUrl(String forwardUrl)。
这里我使用了RESTful,故不需要配置服务端的转发,而是配置了另外两处:
successHandler和
failureHandler,
successHandler方法接收一个
AuthenticationSuccessHandler对象,认证通过之后,Spring Security 将调用该对象的
onAuthenticationSuccess方法,类似地,
failureHandler方法接收一个
AuthenticationFailureHandler对象,认证失败之后,将调用该对象的
onAuthenticationFailure方法。
配置完登录相关信息之后,接着配置和登出有关的信息。和配置登录表单提交地址类似,这里需要配置登出请求提交地址,这里调用
logoutUrl方法,指定登出的链接地址,该地址和前面提到的
loginProcessingUrl都不需要自己写,这两个都是全权交由 Spring Security 来处理。当用户请求
logoutUrl方法指定的地址时,Spring Security 将对用户执行登出操作。和前面提到的
successForwardUrl类似,这里提供了
logoutSuccessUrl方法指定登出成功之后转发的地址。不过我用了RESTful,就不再调用此方法,而是调用
logoutSuccessHandler传入
LogoutSuccessHandler对象,登出成功后将调用该对象的
onLogoutSuccess方法。
最后,配置对异常的处理
exceptionHandling,和上面介绍的
successHandler、
failureHandler以及
logoutSuccessHandler差不多,
authenticationEntryPoint接收一个
AuthenticationEntryPoint对象,当用户请求的操作需要登录时,将抛出
AuthenticationException异常,并且将该异常传入到
AuthenticationEntryPoint对象的
commence方法。
accessDeniedHandler方法接收一个
AccessDeniedHandler对象,该对象的
handle方法将在权限不足时调用。
配置完这些,看
configureGlobalSecurity方法,给
AuthenticationManagerBuilder配置一个
UserDetailsService对象,当用户执行登录时,Spring Security 将调用该对象的
loadUserByUsername方法,将
username传入此方法,根据
username获取一个
UserDetails对象。
另外,由于不能在数据库中保存明文密码,这里对密码进行
bcrypt加密后保存,验证密码是否正确时,需要对用户输入的明文密码进行
bcrypt加密后比较密文是否一致,故这里需要提供一个
BCryptPasswordEncoder对象。
复制
@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Value("${api.csrftoken}") private String csrfTokenApi; @Value("${api.login}") private String loginApi; @Value("${api.logout}") private String logoutApi; @Autowired private MyUserDetailsService userDetailsService; @Autowired private PasswordEncoder passwordEncoder; @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests().antMatchers(csrfTokenApi).permitAll() .antMatchers("/api/user/**").access("hasAuthority('USER')") .antMatchers("/api/admin/**").access("hasAuthority('ADMIN')") .antMatchers("/api/dba/**").access("hasAuthority('DBA')") .antMatchers("/api/**").fullyAuthenticated() .and().formLogin().loginProcessingUrl(loginApi) .successHandler(new RestAuthenticationSuccessHandler()) .failureHandler(new RestAuthenticationFailureHandler()) .and().logout().logoutUrl(logoutApi) .logoutSuccessHandler(new RestLogoutSuccessHandler()) .and().exceptionHandling().authenticationEntryPoint(new RestAuthenticationEntryPoint()) .accessDeniedHandler(new RestAccessDeniedHandler()); } @Autowired public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(11); } }
WebAppConfig
因为采用RESTful风格,这里配置响应视图为json格式。复制
@Configuration @EnableWebMvc @ComponentScan(basePackages = "org.xueliang.springsecuritystudy") @PropertySource({"classpath:config.properties"}) public class WebAppConfig extends WebMvcConfigurerAdapter { @Bean public RequestMappingHandlerAdapter requestMappingHandlerAdapter(@Autowired MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter, @Autowired ContentNegotiationManager mvcContentNegotiationManager) { RequestMappingHandlerAdapter requestMappingHandlerAdapter = new RequestMappingHandlerAdapter(); requestMappingHandlerAdapter.setMessageConverters(Collections.singletonList(mappingJackson2HttpMessageConverter)); requestMappingHandlerAdapter.setContentNegotiationManager(mvcContentNegotiationManager); return requestMappingHandlerAdapter; } @Bean public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() { return new MappingJackson2HttpMessageConverter(); } /** * 设置欢迎页 * 相当于web.xml中的 welcome-file-list > welcome-file */ @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addRedirectViewController("/", "/index.html"); } }
WebAppInitializer
Spring Security 架构是完全基于标准的 Servlet 过滤器的,这里我们需要在WebInitializer中引入
DelegatingFilterProxy过滤器。
复制
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { @Override public void onStartup(ServletContext servletContext) throws ServletException { servletContext.addFilter("springSecurityFilterChain", new DelegatingFilterProxy("springSecurityFilterChain")).addMappingForUrlPatterns(null, false, "/api/*"); // 静态资源映射 servletContext.getServletRegistration("default").addMapping("*.html", "*.ico"); super.onStartup(servletContext); } @Override protected Class<?>[] getRootConfigClasses() { return new Class[] { WebAppConfig.class }; } @Override protected Class<?>[] getServletConfigClasses() { return null; } @Override protected String[] getServletMappings() { return new String[] { "/" }; } @Override protected Filter[] getServletFilters() { return new Filter[] { new CharacterEncodingFilter("UTF-8", true) }; } }
Source
本文使用到的项目源码已经放到 Github 上,你可以下载后运行。相关文章推荐
- Spring MVC + Security 4 初体验(Java配置版)
- Spring MVC + Security 4 初体验(Java配置版)
- 【JavaWeb】Spring+SpringMVC+MyBatis+SpringSecurity+EhCache+JCaptcha 完整Web基础框架(三)
- [Spring实战系列] - No.12 Java配置 SpringMVC + Spring Security + Hibernate 多用户登录
- 【JavaWeb】Spring+SpringMVC+MyBatis+SpringSecurity+EhCache+JCaptcha 完整Web基础框架(四)
- 【JavaWeb】Spring+SpringMVC+MyBatis+SpringSecurity+EhCache+JCaptcha 完整Web基础框架(一)
- java电子商务系统源码 Spring MVC+mybatis+spring cloud+spring boot+spring security
- 【JavaWeb】Spring+SpringMVC+MyBatis+SpringSecurity+EhCache+JCaptcha 完整Web基础框架(二)
- 【JavaWeb】Spring+SpringMVC+MyBatis+SpringSecurity+EhCache+JCaptcha 完整Web基础框架(五)
- 【JavaWeb】Spring+SpringMVC+MyBatis+SpringSecurity+EhCache+JCaptcha 完整Web基础框架(前言)
- spring-security3.1 + spring mvc + Hibernate 控制系统权限
- springmvc+hibernate+security整合笔记
- Spring3 MVC 初体验
- How to check “hasRole” in Java Code with Spring Security?
- springMVC+Java验证码完善注册功能
- spring Caused by: java.lang.SecurityException: Prohibited package name: java.time
- Java系列--第一篇 Maven+Spring+Spring MVC+mybatis 示例
- [ java ] spring-mvc!
- webloigc 使用 spring-loaded 报错,java.lang.SecurityException: 无法定位登录配置
- spring的mvc开发模式-jsp和java交互