Java Web系列:Spring Security 基础
Spring Security虽然比JAAS进步很大,但还是先天不足,达不到ASP.NET中的认证和授权的方便快捷。这里演示登录、注销、记住我的常规功能,认证上自定义提供程序避免对数据库的依赖,授权上自定义提供程序消除从缓存加载角色信息造成的角色变更无效副作用。
1.基于java config的Spring Security基础配置
(1)使用AbstractSecurityWebApplicationInitializer集成到Spring MVC
public class SecurityInitializer extends AbstractSecurityWebApplicationInitializer {
}
(2)使用匿名类在WebSecurityConfigurerAdapter自定义AuthenticationProvider、UserDetailsService、SecurityContextRepository。
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/account**", "/admin**").authenticated();
http.formLogin().usernameParameter("userName").passwordParameter("password").loginPage("/login")
.loginProcessingUrl("/login").successHandler(new SavedRequestAwareAuthenticationSuccessHandler()).and()
.logout().logoutUrl("/logout").logoutSuccessUrl("/");
http.rememberMe().rememberMeParameter("rememberMe");
http.csrf().disable();
http.setSharedObject(SecurityContextRepository.class, new SecurityContextRepository() { private HttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository(); @Override
public SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder) {
SecurityContext context = this.repo.loadContext(requestResponseHolder);
if (context != null && context.getAuthentication() != null) {
Membership membership = new Membership();
String username = context.getAuthentication().getPrincipal().toString();
String[] roles = membership.getRoles(username);
context.getAuthentication().getAuthorities();
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username,
"password", convertStringArrayToAuthorities(roles));
context.setAuthentication(token);
System.out.println("check user role");
}
return context;
} @Override
public void saveContext(SecurityContext context, HttpServletRequest request, HttpServletResponse response) {
this.repo.saveContext(context, request, response);
} @Override
public boolean containsContext(HttpServletRequest request) {
return this.repo.containsContext(request);
}
});
} @Autowired
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(new AuthenticationProvider() { @Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
Membership membership = new Membership();
String username = authentication.getName();
String password = authentication.getCredentials().toString();
if (membership.validateUser(username, password)) {
String[] roles = membership.getRoles(username);
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username,
"password", convertStringArrayToAuthorities(roles));
return token;
}
return null;
} @Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
} });
auth.userDetailsService(new UserDetailsService() { @Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Membership membership = new Membership();
if (membership.hasUser(username)) {
UserDetails user = new User(username, "password",
convertStringArrayToAuthorities(membership.getRoles(username)));
return user;
}
return null;
}
});
} public Collection<? extends GrantedAuthority> convertStringArrayToAuthorities(String[] roles) {
List<SimpleGrantedAuthority> list = new ArrayList<SimpleGrantedAuthority>();
for (String role : roles) {
list.add(new SimpleGrantedAuthority(role));
}
return list;
}
}
2.使用@PreAuthorize在Controller级别通过角色控制权限
(1)使用@PreAuthorize("isAuthenticated()")注解验证登录
@PreAuthorize("isAuthenticated()")
@ResponseBody
@RequestMapping(value = "/account")
public String account() {
return "account";
}
(2)使用@PreAuthorize("hasAuthority('admin')")注解验证角色
@PreAuthorize("hasAuthority('admin')")
@ResponseBody
@RequestMapping("/admin")
public String admin() {
return "admin";
}
3.登录和注销功能
注销直接使用内置功能,登录可以自定义控制器和视图。
@RequestMapping(value = "/login")
public String login(@RequestParam(value = "error", required = false) String error,
@ModelAttribute("model") UserModel model, BindingResult result) {
if (error != null) {
result.rejectValue("userName", "", "Invalid username and password!");
}
return "login";
}
视图:
<%@ page language="java" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib uri="http://www.springframework.org/tags" prefix="s"%>
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form"%>
<!DOCTYPE HTML>
<html>
<head>
<title>Getting Started: Serving Web Content</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<h2>Login</h2>
<form:form modelAttribute="model">
<s:bind path="*">
<c:if test="${status.error}">
<div id="message" class="error">Form has errors</div>
</c:if>
</s:bind>
<div>
<form:label path="userName">userName</form:label>
<form:input path="userName" />
<form:errors path="userName" cssClass="error" />
</div>
<div>
<form:label path="password">password</form:label>
<form:password path="password" />
<form:errors path="password" cssClass="error" />
</div>
<div>
<form:label path="rememberMe">rememberMe</form:label>
<form:checkbox path="rememberMe" />
</div>
<input type="submit" value="submit">
</form:form>
</html>
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只是作为演示,实际上不会被调用。
UserDetails userDetails =
(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这些东西至少要对其基础配置和核心对象有整体上的把握,至少要能快速定位开发中遇到的问题并基于对源代码的了解能应对实际中出现的大部分技术问题才行。
Java Web系列:Spring Security 基础的更多相关文章
- Java Web系列:JDBC 基础
ADO.NET在Java中的对应技术是JDBC,企业库DataAccessApplicationBlock模块在Java中的对应是spring-jdbc模块,EntityFramework在Java中 ...
- Java Web系列:Hibernate 基础
从以下5个方面学习hibernate ORM. (1)配置文件:hibernate.cfg.xml XML文件和hibernate.properties属性文件 (2)实体映射:1对多.多对多 (3) ...
- Java Web系列:Spring Boot 基础 Spring Security基本使用
@OneToOne or @ManyToOne Caused by: org.hibernate.AnnotationException: @OneToOne or @ManyToOne on com ...
- Java Web系列:Spring MVC基础
1.Web MVC基础 MVC的本质是表现层模式,我们以视图模型为中心,将视图和控制器分离出来.就如同分层模式一样,我们以业务逻辑为中心,把表现层和数据访问层代码分离出来是一样的方法.框架只能在技术层 ...
- Java Web系列:Spring Boot 基础
Spring Boot 项目(参考1) 提供了一个类似ASP.NET MVC的默认模板一样的标准样板,直接集成了一系列的组件并使用了默认的配置.使用Spring Boot 不会降低学习成本,甚至增加了 ...
- Java Web系列:Spring Boot 基础 (转)
Spring Boot 项目(参考1) 提供了一个类似ASP.NET MVC的默认模板一样的标准样板,直接集成了一系列的组件并使用了默认的配置.使用Spring Boot 不会降低学习成本,甚至增加了 ...
- Java Web系列:Spring依赖注入基础
一.Spring简介 1.Spring简化Java开发 Spring Framework是一个应用框架,框架一般是半成品,我们在框架的基础上可以不用每个项目自己实现架构.基础设施和常用功能性组件,而是 ...
- Java Web系列:Java Web 项目基础
1.Java Web 模块结构 JSP文件和AXPX文件类似,路径和URL一一对应,都会被动态编译为单独class.Java Web和ASP.NET的核心是分别是Servlet和IHttpHandle ...
- 【JavaEE】SSH+Spring Security基础上配置AOP+log4j
Spring Oauth2大多数情况下还是用不到的,主要使用的还是Spring+SpringMVC+Hibernate,有时候加上SpringSecurity,因此,本文及以后的文章的example中 ...
随机推荐
- Lucene根据字段进行自定义搜索扩展
最近需要对公司的产品搜索功能做一步改动,搜索到的结果首先按照是否有库存进行排序,然后再按照销量.由于库存量也是一个整数,如果直接按照库存量进行倒序排序的话,是不符合要求的,Lucene也没有支持我们这 ...
- 十.jQuery源码分析之.map()
763行:三个参数. elems:待遍历的数组或对象. callback:回调函数,会在数组的每个元素或对象的每个属性上执行.执行时传入两个参数:数组元素,元素下标;或属性名,属性值. arg:仅限于 ...
- SpringFox swagger2 and SpringFox swagger2 UI 接口文档生成与查看
依赖: <!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger2 --> <dependency ...
- Redis Windows环境安装
1.下载Windows 版本 Redis: https://github.com/ServiceStack/redis-windows 2. 解压文件: F:\开源代码学习\01_Redis 打开 目 ...
- #用openfiledialog文件和savefileDialog打开和保存文件
一.打开文件 Stream myStream = null; OpenFileDialog openFileDialog1 = new OpenFileDialog(); ...
- Vue项目中将table组件导出Excel表格以及打印页面内容
体验更优排版请移步原文:http://blog.kwin.wang/programming/vue-table-export-excel-and-print.html 页面中显示的table表格,经常 ...
- PHP截取中文不乱吗
function utf_substr($str, $len) { for ($i = 0; $i < $len; $i++) { $temp_str = substr($str, 0, 1); ...
- 使用API调用外部程序并监控程序状态
Public Type SHELLEXECUTEINFO cbSize As Long fMask As Long hwnd As Long lpVerb As String ...
- 几组不错的X264自定义编码<转>
转帖地址:http://tieba.baidu.com/p/4201033507 一般直播时使用A设定即可.你尝试设置并找出你最满意的设定 A为最需最低CPU资源,E为最高. A8x8dct=1 aq ...
- _MainTex_TexelSize what's the meaning?
uniform float4 _MainTex_TexelSize where is the value of the float4 _MainTexelSize from? It's set by ...