Spring MVC + Security 4 初体验(Java配置版)
spring Version = 4.3.6.RELEASE
springSecurityVersion = 4.2.1.RELEASE
Gradle 3.0 + Eclipse Neno(4.6)
这篇文章同样是使用的Java配置,而非XML配置,如果你对于Java配置的Spring MVC开发还不太熟悉,可以先看我这篇文章。
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 上,你可以下载后运行。
原文链接:http://xueliang.org/article/detail/20170302232815082
Spring MVC + Security 4 初体验(Java配置版)的更多相关文章
- 【原创】Spring MVC项目搭建(使用Java配置)
一.使用Intellij idea,新建maven项目,选择maven-archetype-webapp. 二.在src/main下新建文件夹,命名为java,并标注为source folder. 三 ...
- (4.1)Spring MVC执行原理和基于Java的配置过程
一.Spring MVC执行原理和基于Java配置的配置过程 (一)Spring MVC执行过程,大致为7步. 所有的请求都会经过Spring的一个单例的DispacherServlet. Dispa ...
- Spring MVC执行原理和基于Java的配置过程
一.Spring MVC执行原理和基于Java配置的配置过程 (一)Spring MVC执行过程,大致为7步. 所有的请求都会经过Spring的一个单例的DispacherServlet. Dispa ...
- Spring MVC内容协商实现原理及自定义配置【享学Spring MVC】
每篇一句 在绝对力量面前,一切技巧都是浮云 前言 上文 介绍了Http内容协商的一些概念,以及Spring MVC内置的4种协商方式使用介绍.本文主要针对Spring MVC内容协商方式:从步骤.原理 ...
- Spring Cloud 负载均衡初体验
目录 服务搭建 1.注册中心--Eureka Server 2.服务提供方--Service Provider 3.服务消费方--Service Consumer 服务消费 Feign 与断路器 Hy ...
- Spring入门(8)-基于Java配置而不是XML
Spring入门(8)-基于Java配置而不是XML 本文介绍如何应用Java配置而不是通过XML配置Spring. 0. 目录 声明一个简单Bean 声明一个复杂Bean 1. 声明一个简单Bean ...
- 215.Spring Boot+Spring Security:初体验
[视频&交流平台] SpringBoot视频:http://t.cn/R3QepWG Spring Cloud视频:http://t.cn/R3QeRZc SpringBoot Shiro视频 ...
- Spring mvc Security安全配置
Spring Security笔记:自定义Login/Logout Filter.AuthenticationProvider.AuthenticationToken SPRING SECURITY ...
- 使用Spring MVC,Mybatis框架等创建Java Web项目时各种前期准备的配置文件内容
1.pom.xml 首先,pom.xml文件,里面包含各种maven的依赖,代码如下: <project xmlns="http://maven.apache.org/POM/4.0. ...
随机推荐
- Struts2框架学习(二) Action
Struts2框架学习(二) Action Struts2框架中的Action类是一个单独的javabean对象.不像Struts1中还要去继承HttpServlet,耦合度减小了. 1,流程 拦截器 ...
- Java数据库连接--JDBC调用存储过程,事务管理和高级应用
相关链接:Jdbc调用存储过程 一.JDBC常用的API深入详解及存储过程的调用 1.存储过程的介绍 我们常用的操作数据库语言SQL语句在执行的时候要先进行编译,然后执行,而存储过程是在大型数据库系统 ...
- css全局样式表
http://blog.csdn.net/baok1592/article/details/6448378
- oracle 查询哪些表分区
如果查询当前用户下得分区表:select * from user_tables where partitioned='YES'如果要查询整个数据库中的分区表:select * from dba_tab ...
- 如何快速定位到Eclipse自动添加的TODO
把自动生成的// TODO ....前面加上todo,这样生成之后就会有编译错误,直接 ctrl+. 就到该位置了,可以删除todo留着// TODO ...,也可以ctrl+d删除一行: 不建议不生 ...
- ajax数据交互(arcgis server)
通过ajax来调用服务器map数据,来实现搜索功能. 效果: 1.我要搜索下中国移动的地理信息: 2.会搜出17条消息,然后把他们分页显示,一页6条: 3.每一页的6天数据,会在map生成一个6条ma ...
- 老司机教你下载tumblr上视频和图片的正确姿势
本文面向初学者. 很多同学问我:“我非常想学Python编程,但是找不到兴趣点”. 还有的同学呢,找到了很好的兴趣点,但是无从下手,“玄魂老师,我想下载tumblr上的视频, 怎么下载,Python能 ...
- 【译】延迟加载JavaScript
[译]延迟加载JavaScript 看到一个微信面试题引发的血案 --[译] 什么阻塞了 DOM?中提到的一篇文章,于是决定看下其博客内容,同时翻译下来留作笔记,因英文有限,如有不足之处,欢迎指出.同 ...
- CodeForces 721B
B. Passwords time limit per test:2 seconds memory limit per test:256 megabytes input:standard input ...
- KB奇遇记(7):不靠谱的项目实施计划
在ERP项目启动前期,项目组两方项目经理和我等几个人单独跟总裁开会,讨论了初步的ERP实施计划,本来第一期上线只是考虑上其中一家工厂而已,结果临时加入了深加工的工厂.本来项目组预定计划是2017年1月 ...