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中 ...
随机推荐
- Unreal Enginer4特性介绍
转自:http://blog.csdn.net/cartzhang/article/details/39401991 一.特性说明 特性 Ue4是一款专业开发高质量游戏的平台开发工具.Ue4的渲染加快 ...
- ORM sqlachemy
本节内容 ORM介绍 sqlalchemy安装 sqlalchemy基本使用 多外键关联 多对多关系 表结构设计作业 1. ORM介绍 orm英文全称object relational mapping ...
- 获取中文的首字母demo
import net.sourceforge.pinyin4j.PinyinHelper; /** * * @Title: getPinYinHeadChar * @Description: TODO ...
- 《Linux内核精髓:精通Linux内核必会的75个绝技》一HACK #6 使用localmodconfig缩短编译时间
HACK #6 使用localmodconfig缩短编译时间 本节介绍使用make localmodconfig生成精简的.config文件,缩短内核编译时间的方法.为了能够应对各种各样的环境,发布版 ...
- uva-10879-因数分解
把一个数分解成n*m的形式,一定存在 解题思路: 一个大于1的正整数最小因数一定是素数 ac时间80ms,感觉慢了,可惜看不到0ms的大神代码 #include <iostream> #i ...
- leetcode131
深度优先遍历(DFS),先判断前一个部分是否是回文,如果是,则将其加进集合中,然后继续判断后面的回文串. 在回溯的时候,将之前加入集合的串删除,重新选择回文串.每到达一次叶子节点,得到一组结果. pu ...
- 使用 Zend_Studio 开发
之前一直用vim 写PHP, 总觉得IDE的一大堆没有用的插件非常麻烦,所以一直避免使用Zend_Studio.不过随着PHP的发展和框架的发展,以及个人的发展,最后还是回到的IDE的时代. 在使用Z ...
- Word编写代码时输出半角引号
工具--自动更正选项--键入时自动套用格式,去掉直引号替换为弯引号.
- 76. Minimum Window Substring (String, Map)
Given a string S and a string T, find the minimum window in S which will contain all the characters ...
- 30-python3 中 bytes 和 string 之间的互相转换
转自:http://www.jb51.net/article/105064.htm password = b'123456' 等价于: pw = '123456' password = pw.enco ...