博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java Web系列:Spring Security 基础
阅读量:7088 次
发布时间:2019-06-28

本文共 8762 字,大约阅读时间需要 29 分钟。

Spring Security虽然比JAAS进步很大,但还是先天不足,达不到ASP.NET中的认证和授权的方便快捷。这里演示登录、注销、记住我的常规功能,认证上自定义提供程序避免对数据库的依赖,授权上自定义提供程序消除从缓存加载角色信息造成的角色变更无效副作用。

1.基于java config的Spring Security基础配置

(1)使用AbstractSecurityWebApplicationInitializer集成到Spring MVC

1 public class SecurityInitializer extends AbstractSecurityWebApplicationInitializer {2 }

(2)使用匿名类在WebSecurityConfigurerAdapter自定义AuthenticationProvider、UserDetailsService、SecurityContextRepository。

1 @EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true) 2 @EnableWebSecurity 3 public class SecurityConfig extends WebSecurityConfigurerAdapter { 4  5     @Override 6     protected void configure(HttpSecurity http) throws Exception { 7         http.authorizeRequests().antMatchers("/account**", "/admin**").authenticated(); 8         http.formLogin().usernameParameter("userName").passwordParameter("password").loginPage("/login") 9                 .loginProcessingUrl("/login").successHandler(new SavedRequestAwareAuthenticationSuccessHandler()).and()10                 .logout().logoutUrl("/logout").logoutSuccessUrl("/");11         http.rememberMe().rememberMeParameter("rememberMe");12         http.csrf().disable();13         http.setSharedObject(SecurityContextRepository.class, new SecurityContextRepository() {14 15             private HttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();16 17             @Override18             public SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder) {19                 SecurityContext context = this.repo.loadContext(requestResponseHolder);20                 if (context != null && context.getAuthentication() != null) {21                     Membership membership = new Membership();22                     String username = context.getAuthentication().getPrincipal().toString();23                     String[] roles = membership.getRoles(username);24                     context.getAuthentication().getAuthorities();25                     UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username,26                             "password", convertStringArrayToAuthorities(roles));27                     context.setAuthentication(token);28                     System.out.println("check user role");29                 }30                 return context;31             }32 33             @Override34             public void saveContext(SecurityContext context, HttpServletRequest request, HttpServletResponse response) {35                 this.repo.saveContext(context, request, response);36             }37 38             @Override39             public boolean containsContext(HttpServletRequest request) {40                 return this.repo.containsContext(request);41             }42         });43     }44 45     @Autowired46     @Override47     protected void configure(AuthenticationManagerBuilder auth) throws Exception {48         auth.authenticationProvider(new AuthenticationProvider() {49 50             @Override51             public Authentication authenticate(Authentication authentication) throws AuthenticationException {52                 Membership membership = new Membership();53                 String username = authentication.getName();54                 String password = authentication.getCredentials().toString();55                 if (membership.validateUser(username, password)) {56                     String[] roles = membership.getRoles(username);57                     UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username,58                             "password", convertStringArrayToAuthorities(roles));59                     return token;60                 }61                 return null;62             }63 64             @Override65             public boolean supports(Class
authentication) {66 return authentication.equals(UsernamePasswordAuthenticationToken.class);67 }68 69 });70 auth.userDetailsService(new UserDetailsService() {71 72 @Override73 public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {74 Membership membership = new Membership();75 if (membership.hasUser(username)) {76 UserDetails user = new User(username, "password",77 convertStringArrayToAuthorities(membership.getRoles(username)));78 return user;79 }80 return null;81 }82 });83 }84 85 public Collection
convertStringArrayToAuthorities(String[] roles) {86 List
list = new ArrayList
();87 for (String role : roles) {88 list.add(new SimpleGrantedAuthority(role));89 }90 return list;91 }92 }

2.使用@PreAuthorize在Controller级别通过角色控制权限

(1)使用@PreAuthorize("isAuthenticated()")注解验证登录

1     @PreAuthorize("isAuthenticated()")2     @ResponseBody3     @RequestMapping(value = "/account")4     public String account() {5         return "account";6     }

(2)使用@PreAuthorize("hasAuthority('admin')")注解验证角色

1     @PreAuthorize("hasAuthority('admin')")2     @ResponseBody3     @RequestMapping("/admin")4     public String admin() {5         return "admin";6     }

3.登录和注销功能

注销直接使用内置功能,登录可以自定义控制器和视图。

1     @RequestMapping(value = "/login")2     public String login(@RequestParam(value = "error", required = false) String error,3             @ModelAttribute("model") UserModel model, BindingResult result) {4         if (error != null) {5             result.rejectValue("userName", "", "Invalid username and password!");6         }7         return "login";8     }

视图:

1 <%@ page language="java" pageEncoding="UTF-8"%> 2 <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> 3 <%@ taglib uri="http://www.springframework.org/tags" prefix="s"%> 4 <%@ taglib uri="http://www.springframework.org/tags/form" prefix="form"%> 5  6  7  8 Getting Started: Serving Web Content 9 
10 11 12

Login

13
14
15
16
Form has errors
17
18
19
20
userName
21
22
23
24
25
password
26
27
28
29
30
rememberMe
31
32
33
34
35

4.Spring Security核心对象

验证和授权的核心的ASP.NET肯定是HttpModule,Java是Filter,这没什么可说的,到现在两套组合(HttpApplicaiton+HttpModule+HttpHandler)(ServletContext+Filter+Servlet)的核心概念已经熟练了。

(1)安全上下文SecurityContext

ASP.NET中我们可以通过HttpContext.User获取IPrincipal的实例,这是通过HttpModuel(FormsAuthenticationModule)机制实现的。Spring Security中获取SecurityContext也是通过对应的Filter(SecurityContextPersistenceFilter)机制实现的。SecurityContextPersistenceFilter将功能委托给SecurityContextRepository的实例实现,因此我们在上文自定义了SecurityContextRepository实现,刷新其中的角色信息。

(2)身份认证提供程序AuthenticationProvider

AuthenticationProvider对象的authenticate方法验证并返回Authentication对象。Authentication对象是Java的Principal接口的子接口。上文自定义的AuthenticationProvider只是简单的将用户名username作为Authentication的实现类UsernamePasswordAuthenticationToken构造函数的参数传递,如果需要也可以传递其他object,调用时通过SecurityContextHolder.getContext().getAuthentication().getPrincipal()获取。

(3)用户信息提供程序UserDetailsService

只有在AuthenticationProvider的实现中采用了UserDetailsService用于验证,UserDetailsService才是必须的。UserDetailsService返回一个用UserDetails对象。在AuthenticationProvider中调用UserDetailsService,将UserDetails对象作为Principal参数传递给Authentication对象,这样我们可以在Controller中通过如下语句获取UserDetails对象。事实上只需要传递用户名作为Principal参数是最实用的,多搞一个自定义UserDetails还不如自定义POJO从Service中通过用户名返回信息来的干净快捷。即使使用UserDetails对象也不一定要使用UserDetailsService,可以直接在AuthenticationProvider中构造并传递UserDetails对象。上面代码的UserDetailsService只是作为演示,实际上不会被调用。

1 UserDetails userDetails =2  (UserDetails)SecurityContextHolder.getContext().getAuthentication().getPrincipal();

参考

(1)http://docs.spring.io/autorepo/docs/spring-security/3.2.x/guides/hellomvc.html

(2)http://docs.spring.io/spring-security/site/docs/4.0.4.CI-SNAPSHOT/reference/htmlsingle/

(3)http://docs.spring.io/spring/docs/current/spring-framework-reference/html/view.html

JAAS对核心的数据结构不关注,定义了一堆还不如没有的过程依赖,Spring Security虽然改进了易用性,但从JAAS中继承了不切实际的幻想风格。提供核心数据结构和接口就行了,非要从密码加密到用户信息获取一路跑偏到对缓存和数据库都能产生依赖,Spring基于Object的依赖注入已经不适用了,Spring Security又偏离了核心。内置的不合理,外置的很难用,整合这个词就是整一堆框架合起来才能凑合用。什么时候基于类型的依赖注入框架能取代Spring、基于数据结构的验证框架能取代Spring Security,Java Web开发的生产力估计会提高一些。到现在我还没有找到可用的基于注解的前后端统一验证框架,没有这个东西,对于快速制作演示模型简直是灾难。不管怎么说,SSH这些东西至少要对其基础配置和核心对象有整体上的把握,至少要能快速定位开发中遇到的问题并基于对源代码的了解能应对实际中出现的大部分技术问题才行。

转载地址:http://dryql.baihongyu.com/

你可能感兴趣的文章
.NET平台开源项目速览(9)软件序列号生成组件SoftwareProtector介绍与使用
查看>>
干货:史上最实用逃顶绝招十二式!
查看>>
鸟哥Linux私房菜 基础学习篇读书笔记(10):Linux磁盘和文件系统管理(3)
查看>>
简述Session 、Cookie、cache 区别
查看>>
large-scale analysis of malware downloaders
查看>>
pyqt声音输入
查看>>
FMX 模态窗体
查看>>
使用storyboard实现页面跳转,简单的数据传递
查看>>
有些事明显对自己有益,为什么却无法去做?
查看>>
IOS开发基础知识--碎片30
查看>>
C语言编程规范—命名规则
查看>>
批处理-剪切文件夹到指定目录
查看>>
java POi excel 写入大批量数据
查看>>
关于子类对象的构造函数和父类构造函数的执行顺序
查看>>
.NET Core Web 应用部署到 Docker 中运行
查看>>
Saltstack-API(十二)
查看>>
Asp.net Boilerplate源码中NotNullAttribute的用处
查看>>
javascript继承
查看>>
待处理
查看>>
linux下在root用户登陆状态下,以指定用户运行脚本程序实现方式
查看>>