Spring boot+Spring Security 4配置整合实例

来源:互联网 发布:网络考勤机怎么设置 编辑:程序博客网 时间:2024/04/28 13:39

本例所覆盖的内容:

1. 使用Spring Security管理用户身份认证、登录退出

2. 用户密码加密及验证

3. 采用数据库的方式实现Spring Securityremember-me功能

4. 获取登录用户信息。

5.使用Spring Security管理url和权限

 

本例所使用的框架:

1. Spring boot

2. Spring MVC

3. Spring Security

4. Spring Data JPA

5. thymeleaf

6.gradle

一、 整合Spring Security

build.gradle中加入如下片段:

    compile('org.springframework.boot:spring-boot-starter-data-jpa')    compile("org.springframework.boot:spring-boot-starter-thymeleaf")    compile("org.springframework.boot:spring-boot-starter-security")    testCompile("org.springframework.boot:spring-boot-starter-test")    testCompile("org.springframework.security:spring-security-test")

使用Spring Security4的四种方法概述

    那么在Spring Security4的使用中,有4种方法:

  1.     一种是全部利用配置文件,将用户、权限、资源(url)硬编码在xml文件中;
  2.     二种是用户和权限用数据库存储,而资源(url)和权限的对应采用硬编码配置。
  3.     三种是细分角色和权限,并将用户、角色、权限和资源均采用数据库存储,并且自定义过滤器,代替原有的FilterSecurityInterceptor过滤器         并分别实现AccessDecisionManager、InvocationSecurityMetadataSourceService和UserDetailsService,并在配置文件中进行相应配置。
  4.     四是修改spring security的源代码,主要是修改InvocationSecurityMetadataSourceService和UserDetailsService两个类。 前者是将配置文件     或数据库中存储的资源(url)提取出来加工成为url和权限列表的Map供Security使用,后者提取用户名和权限组成一个完整的(UserDetails)User     对象,该对象可以提供用户的详细信息供AuthentationManager进行认证与授权使用。

我们今天来实现一下第三种。

 当然,spring security4毕竟是西方国家的东西,以英文为主,使用习惯和文化的差异共存,况且为了适应大多数Web应用的权限管理,作者将Spring Security4打造的精简而灵活。精简指Spring Security4对用户和权限的表设计的非常简单,并且没有采用数据库来管理资源(URL)。这样的话,对于我们国人用户来说,是个很大的遗憾,这个遗憾甚至能够影响到我们对安全框架的选型。你想啊,在国内大多数项目中,均设置了比较复杂的权限控制,一般就会涉及到用户、角色、资源3张表,若要加上3张表之间的对应关系表2张,得有5张表。

    但是,Spring Security4提供了灵活的扩展方法。具体应该扩展哪些类呢? 或者到底Spring Security3工作的流程如何,你不妨参看下面一篇文章,就会获得
一些启示,网址为:http://www.blogjava.net/SpartaYew/archive/2011/06/15/350630.html, 哈哈,谢谢分享。

还有一个地址很有价值,http://download.csdn.net/detail/muddled/8981809,我就参考着上面的介绍扩展了4个类。

首先来说一下第三种方法的实现流程,我画了一张简易版流程图,帮助大家理解spring security4 的工作机制:


下面我们就来根据这个图中标注出的,重要的几个4个类来配置吧!

  1. 当然要现在application.properties配置文件中配置好数据库。
  2. 开始配置相关的实体类 SysUser.java  SRole.java  SysResource.java SysResourceRole.java
  3. package security.entity;import java.util.Date;import java.util.HashSet;import java.util.Set;import javax.persistence.Column;import javax.persistence.Entity;import javax.persistence.FetchType;import javax.persistence.GeneratedValue;import javax.persistence.GenerationType;import javax.persistence.Id;import javax.persistence.OneToMany;import javax.persistence.Table;import javax.persistence.Temporal;import javax.persistence.TemporalType;@Entity@Table(name = "s_user")//code11public class SysUser implements java.io.Serializable {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)@Column(name = "id", unique = true, nullable = false)private Integer id;@Column(name = "name", length = 120)private String name; //用户名@Column(name = "email", length = 50)private String email;//用户邮箱@Column(name = "password", length = 120)private String password;//用户密码@Temporal(TemporalType.DATE)@Column(name = "dob", length = 10)private Date dob;//时间@OneToMany(fetch = FetchType.EAGER, mappedBy = "SUser")private Set<SysRole> SysRoles = new HashSet<SysRole>(0);// 所对应的角色集合public SysUser() {}public SysUser(String name, String email, String password, Date dob, Set<SysRole> SysRoles) {this.name = name;this.email = email;this.password = password;this.dob = dob;this.SysRoles = SysRoles;}public Integer getId() {return this.id;}public void setId(Integer id) {this.id = id;}public String getName() {return this.name;}public void setName(String name) {this.name = name;}public String getEmail() {return this.email;}public void setEmail(String email) {this.email = email;}public String getPassword() {return this.password;}public void setPassword(String password) {this.password = password;}public Date getDob() {return this.dob;}public void setDob(Date dob) {this.dob = dob;}@OneToMany(fetch = FetchType.EAGER, mappedBy = "SUser")public Set<SysRole> getSysRoles() {return this.SysRoles;}public void setSRoles(Set<SysRole> SysRoles) {this.SysRoles = SysRoles;}}

    package security.entity;import java.util.Date;import javax.persistence.Column;import javax.persistence.Entity;import javax.persistence.FetchType;import javax.persistence.GeneratedValue;import javax.persistence.GenerationType;import javax.persistence.Id;import javax.persistence.JoinColumn;import javax.persistence.ManyToOne;import javax.persistence.Table;//角色表@Entity@Table(name="s_role")public class SysRole {@Id@GeneratedValue(strategy=GenerationType.IDENTITY)@Column (name="id",length=10)private int id;@ManyToOne(fetch = FetchType.LAZY)@JoinColumn(name = "uid", nullable = false)private SysUser SUser;//角色对应的用户实体@Column(name="name",length=100)private String name;//角色名称public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public SysUser getSUser() {return SUser;}public void setSUser(SysUser sUser) {SUser = sUser;}}

    package cn.paybay.ticketManager.entity;import javax.persistence.Column;import javax.persistence.Entity;import javax.persistence.GeneratedValue;import javax.persistence.GenerationType;import javax.persistence.Id;import javax.persistence.Table;@Entity@Table(name="s_resource")public class SysResource {@Id@GeneratedValue(strategy=GenerationType.IDENTITY)@Column (name="id",length=10)private int id;@Column(name="resourceString",length=1000)private String resourceString;//url@Column(name="resourceId",length=50)private String resourceId;//资源ID@Column(name="remark",length=200)private String remark;//备注@Column(name="resourceName",length=400)private String resourceName;//资源名称@Column(name="methodName",length=400)private String methodName;//资源所对应的方法名@Column(name="methodPath",length=1000)private String methodPath;//资源所对应的包路径public int getId() {return id;}public void setId(int id) {this.id = id;}public String getResourceString() {return resourceString;}public void setResourceString(String resourceString) {this.resourceString = resourceString;}public String getResourceId() {return resourceId;}public void setResourceId(String resourceId) {this.resourceId = resourceId;}public String getRemark() {return remark;}public void setRemark(String remark) {this.remark = remark;}public String getResourceName() {return resourceName;}public void setResourceName(String resourceName) {this.resourceName = resourceName;}public String getMethodName() {return methodName;}public void setMethodName(String methodName) {this.methodName = methodName;}public String getMethodPath() {return methodPath;}public void setMethodPath(String methodPath) {this.methodPath = methodPath;}}

    package security.entity;import java.util.Date;import javax.persistence.Column;import javax.persistence.Entity;import javax.persistence.FetchType;import javax.persistence.GeneratedValue;import javax.persistence.GenerationType;import javax.persistence.Id;import javax.persistence.JoinColumn;import javax.persistence.ManyToOne;import javax.persistence.Table;@Entity@Table(name="s_resource_role")public class SysResourceRole {@Id@GeneratedValue(strategy=GenerationType.IDENTITY)@Column (name="id",length=10)private int id;@Column(name="roleId",length=50)private String roleId; //角色ID@Column(name="resourceId",length=50)private String resourceId;//资源ID@Column(name="updateTime")private Date updateTime;//更新时间public int getId() {return id;}public void setId(int id) {this.id = id;}public String getRoleId() {return roleId;}public void setRoleId(String roleId) {this.roleId = roleId;}public String getResourceId() {return resourceId;}public void setResourceId(String resourceId) {this.resourceId = resourceId;}public Date getUpdateTime() {return updateTime;}public void setUpdateTime(Date updateTime) {this.updateTime = updateTime;}}

    好了实体类都建好了。然后运行一下,springboot程序。hibernate会自动创建表。在表中插入几条测试数据:
  4. s_user表
  5. s_role表

     6.s_resource表


      7.s_resource_role表


//请勿手工写入数据 供remember-me功能使用CREATE TABLE `persistent_logins` (  `username` varchar(64) NOT NULL,  `series` varchar(64) NOT NULL,  `token` varchar(64) NOT NULL,  `last_used` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,  PRIMARY KEY (`series`))

好了,现在我们来配置一下用户和角色的认证吧。

1、首先,创建WebSecurityConfig.java配置类,其中不明所以的地方请参照上面的图,慢慢往下看。

package security;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;import org.springframework.security.config.annotation.web.builders.HttpSecurity;import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;import org.springframework.security.config.annotation.web.servlet.configuration.EnableWebMvcSecurity;import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;import security.support.CustomUserDetailsService;import security.support.LoginSuccessHandler;@Configuration@EnableWebSecuritypublic class WebSecurityConfig extends WebSecurityConfigurerAdapter {@Autowiredprivate CustomUserDetailsService customUserDetailsService;//http://localhost:8080/login 输入正确的用户名密码 并且选中remember-me 则登陆成功,转到 index页面 //再次访问index页面无需登录直接访问//访问http://localhost:8080/home 不拦截,直接访问,//访问http://localhost:8080/hello 需要登录验证后,且具备 “ADMIN”权限hasAuthority("ADMIN")才可以访问@Overrideprotected void configure(HttpSecurity http) throws Exception {http.authorizeRequests().antMatchers("/home").permitAll()//访问:/home 无需登录认证权限.anyRequest().authenticated() //其他所有资源都需要认证,登陆后访问.antMatchers("/hello").hasAuthority("ADMIN") //登陆后之后拥有“ADMIN”权限才可以访问/hello方法,否则系统会出现“403”权限不足的提示.and().formLogin().loginPage("/login")//指定登录页是”/login”.permitAll().successHandler(loginSuccessHandler()) //登录成功后可使用loginSuccessHandler()存储用户信息,可选。.and().logout().logoutSuccessUrl("/home") //退出登录后的默认网址是”/home”.permitAll().invalidateHttpSession(true).and().rememberMe()//登录后记住用户,下次自动登录,数据库中必须存在名为persistent_logins的表.tokenValiditySeconds(1209600);}@Autowiredpublic void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {//指定密码加密所使用的加密器为passwordEncoder()//需要将密码加密后写入数据库 auth.userDetailsService(customUserDetailsService).passwordEncoder(passwordEncoder());auth.eraseCredentials(false);}@Beanpublic BCryptPasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder(4);}@Beanpublic LoginSuccessHandler loginSuccessHandler(){return new LoginSuccessHandler();}}

2、CustomUserDetailsService.java

package security.support;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.security.core.userdetails.UserDetails;import org.springframework.security.core.userdetails.UserDetailsService;import org.springframework.security.core.userdetails.UsernameNotFoundException;import org.springframework.stereotype.Component;import security.entity.SysUser;import security.entity.User;import security.service.UserService;@Componentpublic class CustomUserDetailsService implements UserDetailsService {@Autowired  //业务服务类private UserService userService;@Overridepublic UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {        //SysUser对应数据库中的用户表,是最终存储用户和密码的表,可自定义        //本例使用SysUser中的name作为用户名:SysUser user = userService.findByName(userName);if (user == null) {throw new UsernameNotFoundException("UserName " + userName + " not found");}// SecurityUser实现UserDetails并将SysUser的name映射为usernameSecurityUser seu = new SecurityUser(user);return  seu;}}

3、SecurityUser.java

package security.support;import java.util.ArrayList;import java.util.Collection;import java.util.HashSet;import java.util.List;import java.util.Set;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.security.core.GrantedAuthority;import org.springframework.security.core.authority.SimpleGrantedAuthority;import org.springframework.security.core.userdetails.UserDetails;import security.entity.SysRole;import security.entity.SysUser;public class SecurityUser extends SysUser implements UserDetails {private static final long serialVersionUID = 1L;public SecurityUser(SysUser suser) {if(suser != null){this.setId(suser.getId());this.setName(suser.getName());this.setEmail(suser.getEmail());this.setPassword(suser.getPassword());this.setDob(suser.getDob());this.setSysRoles(suser.getSysRoles());}}@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {Collection<GrantedAuthority> authorities = new ArrayList<>();Set<SysRole> userRoles = this.getSysRoles();if(userRoles != null){for (SysRole role : userRoles) {SimpleGrantedAuthority authority = new SimpleGrantedAuthority(role.getName());authorities.add(authority);}}return authorities;}@Overridepublic String getPassword() {return super.getPassword();}@Overridepublic String getUsername() {return super.getName();}@Overridepublic boolean isAccountNonExpired() {return true;}@Overridepublic boolean isAccountNonLocked() {return true;}@Overridepublic boolean isCredentialsNonExpired() {return true;}@Overridepublic boolean isEnabled() {return true;}}

4、LoginSuccessHandler.java

package security.support;import java.io.IOException;import java.util.Set;import javax.servlet.ServletException;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import org.springframework.security.core.Authentication;import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;import security.entity.SysRole;import security.entity.SysUser;public class LoginSuccessHandler extendsSavedRequestAwareAuthenticationSuccessHandler {@Override      public void onAuthenticationSuccess(HttpServletRequest request,              HttpServletResponse response, Authentication authentication) throws IOException,              ServletException {          //获得授权后可得到用户信息   可使用SUserService进行数据库操作        SysUser userDetails = (SysUser)authentication.getPrincipal();         /* Set<SysRole> roles = userDetails.getSysRoles();*/        //输出登录提示信息          System.out.println("管理员 " + userDetails.getName() + " 登录");              System.out.println("IP :"+getIpAddress(request));                      super.onAuthenticationSuccess(request, response, authentication);      }          public String getIpAddress(HttpServletRequest request){            String ip = request.getHeader("x-forwarded-for");            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {                ip = request.getHeader("Proxy-Client-IP");            }            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {                ip = request.getHeader("WL-Proxy-Client-IP");            }            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {                ip = request.getHeader("HTTP_CLIENT_IP");            }            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {                ip = request.getHeader("HTTP_X_FORWARDED_FOR");            }            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {                ip = request.getRemoteAddr();            }            return ip;        }  }
5、MvcConfig.java

package security;import org.springframework.context.annotation.Configuration;import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;@Configurationpublic class MvcConfig extends WebMvcConfigurerAdapter {    @Override    public void addViewControllers(ViewControllerRegistry registry) {    registry.addViewController("/home").setViewName("home");        registry.addViewController("/").setViewName("home");        registry.addViewController("/hello").setViewName("hello");        registry.addViewController("/login").setViewName("login");    }}


6、在resource下面创建templates目录,然后放相关的html文件:

home.html

<!DOCTYPE html><html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">    <head>        <title>Spring Security Example</title>    </head>    <body>        <h1>Welcome!</h1>                <p>Click <a th:href="@{/hello}">here</a> to see a greeting.</p>    </body></html>
hello.html
<!DOCTYPE html><html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"      xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">    <head>        <title>Hello World!</title>    </head>    <body>        <h1 th:inline="text">Hello [[${#httpServletRequest.remoteUser}]]!</h1>        <form th:action="@{/logout}" method="post">            <input type="submit" value="Sign Out"/>        </form>    </body></html>
login.html
<!DOCTYPE html><html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"      xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">    <head>        <title>Spring Security Example </title>    </head>    <body>        <div th:if="${param.error}">            Invalid username and password.        </div>        <div th:if="${param.logout}">            You have been logged out.        </div>        <form th:action="@{/login}" method="post">            <div><label> User Name : <input type="text" name="username"/> </label></div>            <div><label> Password: <input type="password" name="password"/> </label></div>            <div><input type="submit" value="Sign In"/></div>            <input type="checkbox" name="remember-me" value="true" th:checked="checked"/><p>Remember me</p>        </form>    </body></html>
接下来是主类:MainApplication.java

package security;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;import security.entity.SysUser;import security.entity.User;import security.service.UserService;import security.Appctx;@SpringBootApplicationpublic class MainApplication{public static void main(String[] args) {//SpringApplication.run(MainApplication.class, args);SpringApplication app=new SpringApplication(MainApplication.class);Appctx.ctx=app.run(args);/*UserService suserService = (UserService) Appctx.ctx.getBean("suserService");SysUser su= suserService.findByName("TEST");        BCryptPasswordEncoder bc=new BCryptPasswordEncoder(4);//将密码加密 可以先设置初始密码:000000         su.setPassword(bc.encode(su.getPassword()));//然后使用密码为key值进行加密,运行主类后,会自动加密密码,可连接数据库查看。        System.out.println("密码"+su.getPassword());        suserService.update(su);//运行一次后记得注释这段重复加密会无法匹配*/}}

Appctx.java

package security.support;import org.springframework.context.ApplicationContext;public class Appctx {public static ApplicationContext ctx=null;     public static Object getObject(String string){    return ctx.getBean(string);    }}

1. 运行,访问http://localhost:8080/hello,系统出现如下界面:


登陆成功后:

登陆拥有ADMIN权限的用户,可以进入/home

如果用户不具有权限,会出现以下:

好了,根据配置:.antMatchers("/hello").hasAuthority("ADMIN")。来进行权限控制,就到这里,下面,我们来根据数据库中的资源和权限的关系,进行授权和认证


1、CustomInvocationSecurityMetadataSourceService.java 参照流程图

/* * @(#) MyInvocationSecurityMetadataSourceService.java  2011-3-23 下午02:58:29 * * Copyright 2011 by Sparta  */package security.support;import java.util.ArrayList;import java.util.Collection;import java.util.HashMap;import java.util.Iterator;import java.util.List;import java.util.Map;import java.util.Map.Entry;import java.util.Set;import javax.annotation.PostConstruct;import org.hibernate.Session;import org.hibernate.SessionFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;import org.springframework.security.access.ConfigAttribute;import org.springframework.security.access.SecurityConfig;import org.springframework.security.core.GrantedAuthority;import org.springframework.security.core.context.SecurityContextHolder;import org.springframework.security.core.userdetails.UserDetails;import org.springframework.security.web.FilterInvocation;import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;import org.springframework.security.web.util.matcher.AntPathRequestMatcher;import org.springframework.security.web.util.matcher.RequestMatcher;import org.springframework.stereotype.Component;import org.springframework.stereotype.Service;import security.dao.SResourceVODao;import security.dao.SRoleDao;import security.dao.SRoleVODao;import security.entity.SRole;import security.service.SResourceService;import security.service.SRoleService;/** * 最核心的地方,就是提供某个资源对应的权限定义,即getAttributes方法返回的结果。 此类在初始化时,应该取到所有资源及其对应角色的定义。 *  */@Servicepublic class CustomInvocationSecurityMetadataSourceService implementsFilterInvocationSecurityMetadataSource {@Autowiredprivate SResourceVODao sResourceVODao;@Autowiredprivate SRoleVODao sRoleVODao;private static Map<String, Collection<ConfigAttribute>> resourceMap = null;/*public CustomInvocationSecurityMetadataSourceService(SResourceService sres,SRoleService sR) {this.sResourceService = sres;this.sRoleService = sR;loadResourceDefine();}*/@PostConstruct//  被@PostConstruct修饰的方法会在服务器加载Servle的时候运行,并且只会被服务器执行一次。PostConstruct在构造函数之后执行,init()方法之前执行。private void loadResourceDefine() {   //一定要加上@PostConstruct注解// 在Web服务器启动时,提取系统中的所有权限。List<Map<String,Object>> list =sRoleVODao.findAll();List<String> query = new ArrayList<String>();if(list!=null && list.size()>0) {for(Map<String,Object> sr :list){//String name = sr.get("name")Object value = sr.get("name");String name = String.valueOf(value);query.add(name);}}/* * 应当是资源为key, 权限为value。 资源通常为url, 权限就是那些以ROLE_为前缀的角色。 一个资源可以由多个权限来访问。 * sparta */resourceMap = new HashMap<String, Collection<ConfigAttribute>>();for (String auth : query) {ConfigAttribute ca = new SecurityConfig(auth);//List<Map<String,Object>> query1 = sResourceVODao.findByRoleName(auth);List<String> query1 = new ArrayList<String>();List<Map<String, Object>>  list1 = sResourceVODao.findByRoleName(auth);if(list1!=null && list1.size()>0) {for(Map<String, Object> map :list1){Object value = map.get("resource_string");String url = String.valueOf(value);query1.add(url);}}for (String res : query1) {String url = res;/* * 判断资源文件和权限的对应关系,如果已经存在相关的资源url,则要通过该url为key提取出权限集合,将权限增加到权限集合中。 * sparta */if (resourceMap.containsKey(url)) {Collection<ConfigAttribute> value = resourceMap.get(url);value.add(ca);resourceMap.put(url, value);} else {Collection<ConfigAttribute> atts = new ArrayList<ConfigAttribute>();atts.add(ca);resourceMap.put(url, atts);}}}}@Overridepublic Collection<ConfigAttribute> getAllConfigAttributes() { return new ArrayList<ConfigAttribute>();}// 根据URL,找到相关的权限配置。@Overridepublic Collection<ConfigAttribute> getAttributes(Object object)throws IllegalArgumentException {System.out.println("nwuidhwuiehdfu");// object 是一个URL,被用户请求的url。FilterInvocation filterInvocation = (FilterInvocation) object;if (resourceMap == null) {loadResourceDefine();}Iterator<String> ite = resourceMap.keySet().iterator();while (ite.hasNext()) {String resURL = ite.next(); RequestMatcher requestMatcher = new AntPathRequestMatcher(resURL);    if(requestMatcher.matches(filterInvocation.getHttpRequest())) {return resourceMap.get(resURL);}}return null;}@Overridepublic boolean supports(Class<?> arg0) {return true;}}
2、CustomAccessDecisionManager.java

/* * @(#) MyAccessDecisionManager.java  2011-3-23 下午04:41:12 * * Copyright 2011 by Sparta  */package security.support;import java.util.Collection;import java.util.Iterator;import org.springframework.security.access.AccessDecisionManager;import org.springframework.security.access.AccessDeniedException;import org.springframework.security.access.ConfigAttribute;import org.springframework.security.access.SecurityConfig;import org.springframework.security.authentication.InsufficientAuthenticationException;import org.springframework.security.core.Authentication;import org.springframework.security.core.GrantedAuthority;import org.springframework.stereotype.Component;import org.springframework.stereotype.Service;/** *AccessdecisionManager在Spring security中是很重要的。 * *在验证部分简略提过了,所有的Authentication实现需要保存在一个GrantedAuthority对象数组中。  *这就是赋予给主体的权限。 GrantedAuthority对象通过AuthenticationManager *保存到 Authentication对象里,然后从AccessDecisionManager读出来,进行授权判断。  * *Spring Security提供了一些拦截器,来控制对安全对象的访问权限,例如方法调用或web请求。  *一个是否允许执行调用的预调用决定,是由AccessDecisionManager实现的。  *这个 AccessDecisionManager 被AbstractSecurityInterceptor调用, *它用来作最终访问控制的决定。 这个AccessDecisionManager接口包含三个方法:  * void decide(Authentication authentication, Object secureObject,    List<ConfigAttributeDefinition> config) throws AccessDeniedException; boolean supports(ConfigAttribute attribute); boolean supports(Class clazz);   从第一个方法可以看出来,AccessDecisionManager使用方法参数传递所有信息,这好像在认证评估时进行决定。   特别是,在真实的安全方法期望调用的时候,传递安全Object启用那些参数。   比如,让我们假设安全对象是一个MethodInvocation。   很容易为任何Customer参数查询MethodInvocation,  然后在AccessDecisionManager里实现一些有序的安全逻辑,来确认主体是否允许在那个客户上操作。   如果访问被拒绝,实现将抛出一个AccessDeniedException异常。  这个 supports(ConfigAttribute) 方法在启动的时候被  AbstractSecurityInterceptor调用,来决定AccessDecisionManager  是否可以执行传递ConfigAttribute。   supports(Class)方法被安全拦截器实现调用,  包含安全拦截器将显示的AccessDecisionManager支持安全对象的类型。 */@Servicepublic class CustomAccessDecisionManager implements AccessDecisionManager {public void decide( Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException{if( configAttributes == null ) {return ;}Iterator<ConfigAttribute> ite = configAttributes.iterator();while( ite.hasNext()){ConfigAttribute ca = ite.next();String needRole = ((SecurityConfig)ca).getAttribute();//ga 为用户所被赋予的权限。 needRole 为访问相应的资源应该具有的权限。for( GrantedAuthority ga: authentication.getAuthorities()){if(needRole.trim().equals(ga.getAuthority().trim())){return;}}}throw new AccessDeniedException("权限不足");}public boolean supports( ConfigAttribute attribute ){return true;//都要设为true}public boolean supports(Class<?> clazz){return true;//都要设为true}}

3、 MyFilterSecurityInterceptor.java

/* * @(#) MyFilterSecurityInterceptor.java  2011-3-23 上午07:53:03 * * Copyright 2011 by Sparta  */package security.support;import java.io.IOException;import java.util.Collection;import java.util.Map;import javax.annotation.PostConstruct;import javax.servlet.Filter;import javax.servlet.FilterChain;import javax.servlet.FilterConfig;import javax.servlet.ServletException;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.security.access.ConfigAttribute;import org.springframework.security.access.SecurityMetadataSource;import org.springframework.security.access.intercept.AbstractSecurityInterceptor;import org.springframework.security.access.intercept.InterceptorStatusToken;import org.springframework.security.authentication.AuthenticationManager;import org.springframework.security.web.FilterInvocation;import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;import security.service.SResourceService;/** * 该过滤器的主要作用就是通过spring著名的IoC生成securityMetadataSource。 * securityMetadataSource相当于本包中自定义的MyInvocationSecurityMetadataSourceService。 * 该MyInvocationSecurityMetadataSourceService的作用提从数据库提取权限和资源,装配到HashMap中, * 供Spring Security使用,用于权限校验。 * @author sparta 11/3/29 * */@Componentpublic class MySecurityFilter extends AbstractSecurityInterceptorimplements Filter{@Autowiredprivate CustomInvocationSecurityMetadataSourceService  mySecurityMetadataSource;@Autowiredprivate CustomAccessDecisionManager myAccessDecisionManager;@Autowiredprivate AuthenticationManager authenticationManager;@PostConstructpublic void init(){super.setAuthenticationManager(authenticationManager);super.setAccessDecisionManager(myAccessDecisionManager);}public void doFilter( ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException{FilterInvocation fi = new FilterInvocation( request, response, chain );invoke(fi);}public Class<? extends Object> getSecureObjectClass(){return FilterInvocation.class;}public void invoke( FilterInvocation fi ) throws IOException, ServletException{System.out.println("filter..........................");InterceptorStatusToken  token = super.beforeInvocation(fi);try{fi.getChain().doFilter(fi.getRequest(), fi.getResponse());}finally{super.afterInvocation(token, null);}}@Overridepublic SecurityMetadataSource obtainSecurityMetadataSource(){System.out.println("filtergergetghrthetyetyetyetyj");return this.mySecurityMetadataSource;}public void destroy(){System.out.println("filter===========================end");}public void init( FilterConfig filterconfig ) throws ServletException{System.out.println("filter===========================");}}

接下来修改一个类的 大家注意:
WebSecurityConfig.java

package security;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.autoconfigure.security.SecurityProperties;import org.springframework.boot.web.servlet.FilterRegistrationBean;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.core.annotation.Order;import org.springframework.security.authentication.AuthenticationManager;import org.springframework.security.authentication.AuthenticationProvider;import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;import org.springframework.security.config.annotation.web.builders.HttpSecurity;import org.springframework.security.config.annotation.web.builders.WebSecurity;import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;import org.springframework.security.config.annotation.web.servlet.configuration.EnableWebMvcSecurity;import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;import security.support.CustomUserDetailsService;import security.support.LoginSuccessHandler;import security.support.MySecurityFilter;@Configuration@EnableWebSecuritypublic class WebSecurityConfig extends WebSecurityConfigurerAdapter {@Autowiredprivate MyFilterSecurityInterceptor mySecurityFilter;@Autowiredprivate CustomUserDetailsService customUserDetailsService;@Overridepublic AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); }//http://localhost:8080/login 输入正确的用户名密码 并且选中remember-me 则登陆成功,转到 index页面 //再次访问index页面无需登录直接访问//访问http://localhost:8080/home 不拦截,直接访问,//访问http://localhost:8080/hello 需要登录验证后,且具备 “ADMIN”权限hasAuthority("ADMIN")才可以访问@Overrideprotected void configure(HttpSecurity http) throws Exception {http.addFilterBefore(mySecurityFilter, FilterSecurityInterceptor.class)//在正确的位置添加我们自定义的过滤器.authorizeRequests().antMatchers("/home").permitAll().anyRequest().authenticated()//.antMatchers("/hello").hasAuthority("ADMIN").and().formLogin().loginPage("/login").permitAll().successHandler(loginSuccessHandler())//code3.and().logout().logoutSuccessUrl("/home").permitAll().invalidateHttpSession(true).and().rememberMe().tokenValiditySeconds(1209600);}@Override    public void configure(WebSecurity web) throws Exception {        super.configure(web);}@Autowiredpublic void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {//指定密码加密所使用的加密器为passwordEncoder()//需要将密码加密后写入数据库 auth.userDetailsService(customUserDetailsService).passwordEncoder(passwordEncoder());//不删除凭据,以便记住用户auth.eraseCredentials(false);}// Code5----------------------------------------------@Beanpublic BCryptPasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder(4);}// Code3----------------------------------------------@Beanpublic LoginSuccessHandler loginSuccessHandler(){return new LoginSuccessHandler();}}

ServletInitializer.java

package security;import javax.servlet.FilterRegistration;import javax.servlet.ServletContext;import javax.servlet.ServletException;import org.springframework.boot.builder.SpringApplicationBuilder;import org.springframework.boot.web.support.SpringBootServletInitializer;import org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter;public class ServletInitializer extends SpringBootServletInitializer {@Overrideprotected SpringApplicationBuilder configure(SpringApplicationBuilder application) {return application.sources(MainApplication.class);}@Overridepublic void onStartup(ServletContext servletContext)throws ServletException { FilterRegistration.Dynamic openEntityManagerInViewFilter = servletContext.addFilter("openEntityManagerInViewFilter", OpenEntityManagerInViewFilter.class);     openEntityManagerInViewFilter.setInitParameter("entityManagerFactoryBeanName","entityManagerFactory");     openEntityManagerInViewFilter.addMappingForUrlPatterns(null, false, "/*");super.onStartup(servletContext);}}

还有主类需要修改

package security;import java.io.IOException;import java.util.HashMap;import java.util.List;import java.util.Map;import java.util.Set;import javax.annotation.PostConstruct;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.EnableAutoConfiguration;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;import org.springframework.web.method.HandlerMethod;import org.springframework.web.servlet.mvc.method.RequestMappingInfo;import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;import security.entity.SysResource;import security.service.SResourceService;import csecurity.service.UserService;import security.support.MySecurityFilter;@SpringBootApplication@EnableAutoConfiguration(exclude = MyFilterSecurityInterceptor.class) //注意public class MainApplication{@Autowiredprivate SResourceService sresourceService;private static final Logger log = LoggerFactory.getLogger(MainApplication.class);@PostConstruct public void initApplication() throws IOException {     log.info("Running with Spring profile(s) : {}"); } public static void main(String[] args) {//SpringApplication.run(MainApplication.class, args);SpringApplication app=new SpringApplication(MainApplication.class);Appctx.ctx=app.run(args);/*UserService suserService = (UserService) Appctx.ctx.getBean("suserService");SysUser su= suserService.findByName("user");        System.out.println("密码"+su.getPassword());        System.out.println("名字"+su.getName());        BCryptPasswordEncoder bc=new BCryptPasswordEncoder(4);//将密码加密        su.setPassword(bc.encode(su.getPassword()));        System.out.println("密码"+su.getPassword());        suserService.update(su);*/}}

至此,我们spring security4就集成成功了,代码,博主已经验证,如有什么错误的地方,欢迎提出。谢谢!
还有资源表中的url可以使用spring的RequestMappingHandlerMapping类自动扫描入库,具体请见http://blog.csdn.net/code__code/article/details/53886912。

2 0