本例所覆盖的内容:
1. 使用Spring Security管理用户身份认证、登录退出
2. 用户密码加密及验证
3. 采用数据库的方式实现Spring Security的remember-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种方法:
我们今天来实现一下第三种。
当然,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个类来配置吧!
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")//code11
public 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 SysRoles = new HashSet(0);// 所对应的角色集合
public SysUser() {
}
public SysUser(String name, String email, String password, Date dob, Set 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 getSysRoles() {
return this.SysRoles;
}
public void setSRoles(Set 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;
}
}
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
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private CustomUserDetailsService customUserDetailsService;
//http://localhost:8080/login 输入正确的用户名密码 并且选中remember-me 则登陆成功,转到 index页面
//再次访问index页面无需登录直接访问
//访问http://localhost:8080/home 不拦截,直接访问,
//访问http://localhost:8080/hello 需要登录验证后,且具备 “ADMIN”权限hasAuthority("ADMIN")才可以访问
@Override
protected 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);
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
//指定密码加密所使用的加密器为passwordEncoder()
//需要将密码加密后写入数据库
auth.userDetailsService(customUserDetailsService).passwordEncoder(passwordEncoder());
auth.eraseCredentials(false);
}
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(4);
}
@Bean
public LoginSuccessHandler loginSuccessHandler(){
return new LoginSuccessHandler();
}
}
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;
@Component
public class CustomUserDetailsService implements UserDetailsService {
@Autowired //业务服务类
private UserService userService;
@Override
public 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映射为username
SecurityUser seu = new SecurityUser(user);
return seu;
}
}
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());
}
}
@Override
public Collection extends GrantedAuthority> getAuthorities() {
Collection authorities = new ArrayList<>();
Set userRoles = this.getSysRoles();
if(userRoles != null)
{
for (SysRole role : userRoles) {
SimpleGrantedAuthority authority = new SimpleGrantedAuthority(role.getName());
authorities.add(authority);
}
}
return authorities;
}
@Override
public String getPassword() {
return super.getPassword();
}
@Override
public String getUsername() {
return super.getName();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
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 extends
SavedRequestAwareAuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response, Authentication authentication) throws IOException,
ServletException {
//获得授权后可得到用户信息 可使用SUserService进行数据库操作
SysUser userDetails = (SysUser)authentication.getPrincipal();
/* Set 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;
@Configuration
public 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");
}
}
home.html
Spring Security Example
Welcome!
Click here to see a greeting.
hello.html
Hello World!
Hello [[${#httpServletRequest.remoteUser}]]!
login.html
Spring Security Example
Invalid username and password.
You have been logged out.
接下来是主类: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;
@SpringBootApplication
public 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);//运行一次后记得注释这段重复加密会无法匹配*/
}
}
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方法返回的结果。 此类在初始化时,应该取到所有资源及其对应角色的定义。
*
*/
@Service
public class CustomInvocationSecurityMetadataSourceService implements
FilterInvocationSecurityMetadataSource {
@Autowired
private SResourceVODao sResourceVODao;
@Autowired
private SRoleVODao sRoleVODao;
private static Map> 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
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 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支持安全对象的类型。 */
@Service
public class CustomAccessDecisionManager implements AccessDecisionManager {
public void decide( Authentication authentication, Object object,
Collection configAttributes)
throws AccessDeniedException, InsufficientAuthenticationException{
if( configAttributes == null ) {
return ;
}
Iterator 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
*
*/
@Component
public class MySecurityFilter
extends AbstractSecurityInterceptor
implements Filter{
@Autowired
private CustomInvocationSecurityMetadataSourceService mySecurityMetadataSource;
@Autowired
private CustomAccessDecisionManager myAccessDecisionManager;
@Autowired
private AuthenticationManager authenticationManager;
@PostConstruct
public 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);
}
}
@Override
public 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===========================");
}
}
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
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private MyFilterSecurityInterceptor mySecurityFilter;
@Autowired
private CustomUserDetailsService customUserDetailsService;
@Override
public 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")才可以访问
@Override
protected 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);
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
//指定密码加密所使用的加密器为passwordEncoder()
//需要将密码加密后写入数据库
auth.userDetailsService(customUserDetailsService).passwordEncoder(passwordEncoder());
//不删除凭据,以便记住用户
auth.eraseCredentials(false);
}
// Code5----------------------------------------------
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(4);
}
// Code3----------------------------------------------
@Bean
public LoginSuccessHandler loginSuccessHandler(){
return new LoginSuccessHandler();
}
}
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 {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(MainApplication.class);
}
@Override
public 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{
@Autowired
private 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);*/
}
}