spring security4.x改造以及工作流程

spring security4.x配置以及工作流程

一、资源过滤器WebSecurityConfig.java
/**
* @since 2017-11-08
* @author wwl
*
*/
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true) //启用Security注解
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private MySecurityFilter 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.addFilterAt(myUsernamePasswordAuthenticationFilter(), MyUsernamePasswordAuthenticationFilter.class).exceptionHandling();
    //http.exceptionHandling().accessDeniedHandler(accessDeniedHandler()).and().authorizeRequests().anyRequest().authenticated().expressionHandler(webSecurityExpressionHandler());  
    http   
    .authorizeRequests()  
    .antMatchers("/mystatic/**").permitAll()   //对静态资源放行(静态资源放在src/main/resources/static)
    .antMatchers("/enterLoginPage").permitAll()//访问:/enterLoginPage 无需登录认证权限  
    .antMatchers("/userLogin").permitAll()   //用户名和密码验证接口无需认证
    .antMatchers("/enterForgetPasswordPage").permitAll()  //忘记密码页面
    .antMatchers("/loginByQQoauth,/AfterLoginRedirect").permitAll()//对某些资源请求进行放行

    .antMatchers("/enterLoginPage").anonymous()   //匿名用户可以访问 
    //.anyRequest().authenticated() //其他所有资源都需要认证,登陆后访问(不是登录状态不可以访问,这种方式不允许匿名访问)  
    .antMatchers("/enterAdminPage").hasAuthority("ROLE_ADMIN") //登陆后之后拥有“ROLE_ADMIN”权限才可以访问/hello方法,否则系统会出现“403”权限不足的提示  
    .and()
    .formLogin()   
    .loginPage("/enterLoginPage")//指定登录页是”login.jsp”  
    //.failureForwardUrl("/enterLoginPage?loginerror=true")    //登录失败后跳转的链接
    .failureUrl("/enterLoginPage?loginerror=yes")
    .usernameParameter("username").passwordParameter("password")    //设置登录的用户名和密码参数
    //.defaultSuccessUrl("/enterIndex", true)
    .successHandler(loginSuccessHandler()) //登录成功后可使用loginSuccessHandler()存储用户信息,可选。  
    //.successForwardUrl("/enterIndex")
    .permitAll()  
    .and()
    .logout() 
    .logoutUrl("/loginout")
    .logoutSuccessHandler(mylogoutSuccessHandler())   //成功退出的处理器
    //.logoutSuccessUrl("/loginout") //退出登录后的默认网址是”/enterLoginPage”  又回到登录界面
    .permitAll()  
    .invalidateHttpSession(true)   
    .and()
    .rememberMe()//登录后记住用户,下次自动登录,数据库中必须存在名为persistent_logins的表  
    .tokenValiditySeconds(1209600)   //rememberme保存的时间(大约保存14天)
    .tokenRepository(tokenRepository())  //指定记住登录信息所使用的数据源
    .userDetailsService(customUserDetailsService)
    .and().csrf().disable();     //先关闭csrf验证
    //当没有权限的时候,进入accessDeniedHandler
    http.exceptionHandling().accessDeniedHandler(myAccessDeniedHandler());
    //只能允许同时一个人登录(另一个人登录同一个账号,会把之前的挤下去)
    http.sessionManagement().maximumSessions(1).expiredUrl("/enterLoginPage");
    http.addFilterBefore(mySecurityFilter, FilterSecurityInterceptor.class);//在正确的位置添加我们自定义的过滤器  
} 




@Override  
public void configure(WebSecurity web) throws Exception {  
    //对静态资源和jsp页面放行
    web.ignoring().antMatchers("/mystatic/**","/*.jsp","/**/*.jsp","/**/**/*.jsp","WEB-INF/jsp/**");
    web.ignoring().antMatchers("/mystatic/**");
}  

@Autowired  
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {     
    //指定密码加密所使用的加密器为passwordEncoder()  
    //需要将密码加密后写入数据库         加密方法 
    //auth.userDetailsService(customUserDetailsService).passwordEncoder(passwordEncoder());
    auth.userDetailsService(customUserDetailsService);   //先不加密
    auth.authenticationProvider(authenticationProvider());  
    auth.eraseCredentials(false);   //不删除凭据,以便记住用户           
}  

//注入数据源
@Autowired
private DataSource dataSource;

//spring security 内部都写死了,这里要把 这个DAO 注入
@Bean
public JdbcTokenRepositoryImpl tokenRepository(){        
    JdbcTokenRepositoryImpl j=new JdbcTokenRepositoryImpl();
    j.setDataSource(dataSource);
    return j;
}

@Bean  
public DaoAuthenticationProvider authenticationProvider() {  
    DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();  
    authenticationProvider.setUserDetailsService(customUserDetailsService);  
    authenticationProvider.setPasswordEncoder(passwordEncoder());  
    return authenticationProvider;  
} 

@Bean   
public BCryptPasswordEncoder passwordEncoder() {  
    return new BCryptPasswordEncoder(4);     //参数为加密的强度,越大说明强度越高,最大为10
}  

@Bean  
public LoginSuccessHandler loginSuccessHandler(){     //登录成功后的处理器
    return new LoginSuccessHandler();  
}  

@Bean
public MylogoutSuccessHandler mylogoutSuccessHandler(){  //成功退出后的处理器
    return new MylogoutSuccessHandler();
}

@Bean
public MyAccessDeniedHandler myAccessDeniedHandler(){   //没有对应权限的时候的处理器(必须要是登录后)
    return new MyAccessDeniedHandler();
}

}

二、安全容器加载、拦截器MySecurityFilter.java
/**
* 该过滤器的主要作用就是通过spring著名的IoC生成securityMetadataSource。
* securityMetadataSource相当于本包中自定义的MyInvocationSecurityMetadataSourceService。
* 该MyInvocationSecurityMetadataSourceService的作用提从数据库提取权限和资源,装配到HashMap中,
* 供Spring Security使用,用于权限校验。
* @author wwl
* @since 2017.11.16
*
*/
@Component
public class MySecurityFilter
extends AbstractSecurityInterceptor
implements Filter{
@Autowired
private FilterInvocationSecurityMetadataSource securityMetadataSource;

@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{  

    HttpServletRequest req = (HttpServletRequest) request;
    HttpServletResponse resp = (HttpServletResponse) response;

    User userDetails = null;
    String username = null;
    try{
        userDetails = (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        username = userDetails.getUsername();

        Object principal =  SecurityContextHolder.getContext().getAuthentication().getPrincipal(); 
        Iterator it = ((UserDetails)principal).getAuthorities().iterator(); 
        String authority = "";      
          while(it.hasNext()){     //账户和角色是多对一关系,所以只迭代一次
            authority = ((GrantedAuthority)it.next()).getAuthority();  

          }  
          req.getSession().setAttribute("role", authority);  //角色名放入session中

    }catch(Exception e){       //这里不能抛出异常
    }
    if(username != null && username != ""){
        req.getSession().setAttribute("username", username);
    }

    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{  
    //fi里面有一个被拦截的url
    //里面调用CustomInvocationSecurityMetadataSourceService的getAttributes(Object object)这个方法获取fi对应的所有权限
    //再调用CustomAccessDecisionManager的decide方法来校验用户的权限是否足够
    //System.out.println("开始过滤请求...");  
    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("加载资源...");  
    return this.securityMetadataSource;  
}  

public void destroy(){  
    System.out.println("filter===========================end");  
}  
public void init( FilterConfig filterconfig ) throws ServletException{  
    System.out.println("filter===========================begin");  
}  

}

三、登录、访问CustomInvocationSecurityMetadataSourceService.java
/**
* 最核心的地方,就是提供某个资源对应的权限定义,即getAttributes方法返回的结果。 此类在初始化时,应该取到所有资源及其对应角色的定义。
* @since 2017-11-08
* @author wwl
*/
@Service
public class CustomInvocationSecurityMetadataSourceService implements
FilterInvocationSecurityMetadataSource {

@Autowired    //自动装配
private UserSecurityService userSecurityService;

private static Map<String, Collection<ConfigAttribute>> resourceMap = null;  


@PostConstruct//被@PostConstruct修饰的方法会在服务器加载Servle的时候运行,并且只会被服务器执行一次。PostConstruct在构造函数之后执行,init()方法之前执行。 
private void loadResourceDefine() {  
    if(resourceMap == null) {  
        resourceMap = new ConcurrentHashMap<String, Collection<ConfigAttribute>>();  
    }else{  
        resourceMap.clear();  
    } 
    // 在Web服务器启动时,提取系统中的所有权限。
    //String为数据库中读取的URL
    Map<String,List<Srole>> resourceRoleMap = userSecurityService.getAllResourceRole();  
    //依次取出所有角色名,加入到Authority集合中
    for (Entry<String,List<Srole>> entry : resourceRoleMap.entrySet()) {  
        String url = entry.getKey();    //获取的url(本地数据库获取的url资源)
        List<Srole> values = entry.getValue();    //获取该URL所具有的角色名 

        Collection<ConfigAttribute> configAttributes = new ArrayList<ConfigAttribute>();  
        for(Srole srole : values){  //角色名放入ConfigAttribute
            ConfigAttribute configAttribute = new SecurityConfig(srole.getRolename());   //吧角色名称加入到配置中  
            configAttributes.add(configAttribute);  
        }  
        resourceMap.put(url, configAttributes);  
    }  
}  

@Override  
public Collection<ConfigAttribute> getAllConfigAttributes() {  
     return new ArrayList<ConfigAttribute>();  
}   
// 根据URL,找到相关的权限配置。
@Override  
public Collection<ConfigAttribute> getAttributes(Object object)  //object是一个链接资源请求
        throws IllegalArgumentException {  
    //System.out.println("进行了请求的拦截....");  
    // object 是一个URL,被用户请求的url。
    FilterInvocation filterInvocation = (FilterInvocation) object;  
    //System.out.println("请求的链接资源:"+filterInvocation.getHttpRequest().getServletPath());

    if (resourceMap == null) {  
        loadResourceDefine();    //从数据库中加载资源权限  
    }  
    Iterator<String> ite = resourceMap.keySet().iterator();  
    while (ite.hasNext()) {     //拦截的URL和数据库中读取的URL进行匹配
        String resURL = ite.next();    //resURL是数据库中读取的链接 
        //System.out.println("从数据库中读取的URL资源:"+resURL);
         RequestMatcher requestMatcher = new AntPathRequestMatcher(resURL);  
            if(requestMatcher.matches(filterInvocation.getHttpRequest())) {  
            //System.out.println("匹配成功...");
            return resourceMap.get(resURL);      //如果匹配成功,那么则返回该URL对应的角色名集合(不止一个)
        }  
    }  
    //System.out.println("没有匹配的资源...");
    //如果数据库中没有相匹配的资源,那么返回空
    return null;  
}  
@Override  
public boolean supports(Class<?> arg0) {  

    return true;  
}  

}

四、获取用户、角色、资源、账号停用信息CustomUserDetailsService.java
/**
* @since 2017-11-09
* @author wwl
*
*/
@Component
public class CustomUserDetailsService implements UserDetailsService {
@Autowired //业务服务类
private UserSecurityService userService;

/**
 * userName为输入的用户名
 */
@Override  
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {  
    //SysUser对应数据库中的用户表,是最终存储用户和密码的表,可自定义  
    //根据用户名(userName)获取角色名称(rolename)

     Map map ;    //数据库中的字段类型不止一种,所以在这里不能使用泛型
     map = userService.getUserRoleByUsernameService(userName);   //根据用户名获取角色表和用户表
     System.out.println("用户登录输入的用户名:"+userName);
     if(map==null){  
        //System.out.println("从数据库中没有查到用户信息...");
        throw new UsernameNotFoundException("member "+userName +" not found.");  
     }else{
        System.out.println("map不为空...."+map.get("userpass"));
     }
     Collection<GrantedAuthority> authorities = new ArrayList<>(); 
     //1:此处将权限信息添加到 GrantedAuthority 对象中,在后面进行全权限验证时会使用GrantedAuthority 对象。
     authorities.add(new SimpleGrantedAuthority((String)map.get("rolename")));   //获取角色名称
     User userdetail = new User((String)map.get("username"), (String)map.get("userpass"),  
             (Integer)map.get("enabled")==1?true:false, // 账号状态 0 表示停用 1表示启用  
                true, true, true, authorities // 用户的权限   (该用户包含的角色名称,本示例中用户和角色是一对一关系)
                );  
     return  userdetail;  
}  

}

五、访问资源决策器CustomAccessDecisionManager.java
/**
*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支持安全对象的类型。
@since 2017-11-08
@author wwl
*/
@Service
public class CustomAccessDecisionManager implements AccessDecisionManager {

public void decide( Authentication authentication, Object object,   
        Collection<ConfigAttribute> configAttributes)   
    throws AccessDeniedException, InsufficientAuthenticationException{  
    if( configAttributes == null ) {     //说明之前请求的链接资源在数据中没有找到,所以getAttributes方法返回空  
        return ;  
    }  

    Iterator<ConfigAttribute> ite = configAttributes.iterator();     //是请求URL所对应的角色名集合,赋值给迭代器 

    while( ite.hasNext()){  
        ConfigAttribute ca = ite.next();  
        String needRole = ((SecurityConfig)ca).getAttribute();      //needRole为单个角色名

    //ga 为用户所被赋予的权限。 needRole 为访问相应的资源应该具有的权限。
        for( GrantedAuthority ga: authentication.getAuthorities()){  

            if(needRole.trim().equals(ga.getAuthority().trim())){  
                //System.out.println("成功匹配,可以访问该资源...");
                return;  
            }    
        }      
    }   
    throw new AccessDeniedException("权限不足");      //权限不足,那么抛出异常

}  

public boolean supports( ConfigAttribute attribute ){  
    return true;//都要设为true  

}  

public boolean supports(Class<?> clazz){  
    return true;//都要设为true
}  

}

六、权限不足处理器MyAccessDeniedHandler.java
/**
* 请求资源没有对应权限的处理器
* @since 2017-11-24
* @author wwl
*
*/
@Component
public class MyAccessDeniedHandler implements AccessDeniedHandler {

private String errorPage = "hasNoRights";

@Override
@ResponseBody
public void handle(HttpServletRequest request, HttpServletResponse response,
        AccessDeniedException accessDeniedException) throws IOException, ServletException {

    //boolean isAjax = ControllerTools.isAjaxRequest(request);  
    if(request.getHeader("x-requested-with")!= null && request.getHeader("x-requested-with").equals("XMLHttpRequest")){ 
        //是ajax请求
        //System.out.println("该请求是ajax请求...不必跳转页面");  
        try {
            String contentType = "application/json";  
            response.setContentType(contentType);  
            String jsonObject="{\"tip\":\"403\"}";  
            //System.out.println("通过ajax方法访问的资源,没有权限");
            PrintWriter out = response.getWriter();  
            out.print(jsonObject);  
            out.flush();  
            out.close();
            return ;
        } catch (IOException e) {
            e.printStackTrace();
        }
    }else if (!response.isCommitted()) {  
        if (errorPage != null) {  
            //System.out.println("不是ajax请求...那么跳转页面");
            // Put exception into request scope (perhaps of use to a view)  
            request.setAttribute(WebAttributes.ACCESS_DENIED_403, accessDeniedException);  

            // Set the 403 status code.  
            response.setStatus(HttpServletResponse.SC_FORBIDDEN);  

            // forward to error page.  
            RequestDispatcher dispatcher = request.getRequestDispatcher(errorPage);  
            dispatcher.forward(request, response);  
        } else {  
            response.sendError(HttpServletResponse.SC_FORBIDDEN, accessDeniedException.getMessage());  
        }  
    }
}

}

七、访问日志记录LoginSuccessHandler.java
/**
* 登录成功后的处理器,应该日志进行记录
* @since 2017-11-08
* @author wwl
*
*/
@Component
public class LoginSuccessHandler extends
SavedRequestAwareAuthenticationSuccessHandler {

@Autowired
private LogLogServiceImpl logLogServiceImpl;

@DateTimeFormat(pattern="yyyy-MM-dd hh:mm:ss")
Date date = new Date();

@Autowired
private UserService userService;


@Override    
public void onAuthenticationSuccess(HttpServletRequest request,    
        HttpServletResponse response, Authentication authentication) throws IOException,    
        ServletException {    
    //获得授权后可得到用户信息   可使用SUserService进行数据库操作  
    User userDetails = (User)authentication.getPrincipal();    
    //输出登录提示信息    
    //System.out.println("用户" + userDetails.getUsername() + " 登录");  
    //登录客户端的IP地址(数据库记录登录日志信息)
    //System.out.println("IP :"+request.getRemoteAddr()+"\t远程客户端主机名:"+request.getRemoteHost());
    LogLog logLog = new LogLog();   //实例化日志bean
    logLog.setUsername(userDetails.getUsername().trim());
    logLog.setRemoteaddr(request.getRemoteAddr().trim());
    logLog.setRemotehost(request.getRemoteHost().trim());
    date = new Date();
    logLog.setLogintime(date);   //加入时间
    logLog.setActions("登录".trim());
    logLogServiceImpl.insertLogLogRecordsService(logLog);

// try{
// request.getRequestDispatcher(“enterIndex”).forward(request, response); //进入网站
// }catch(Exception e){
// System.out.println(“地址跳转时发生了异常”);
// }
//把用户名信息放入session中
Suser suser = new Suser();
suser.setUsername(userDetails.getUsername());
request.getSession().setAttribute(“user”, userService.getUserByUsernameService(suser.getUsername()));

    this.setDefaultTargetUrl("/enterIndex"); 
    this.setAlwaysUseDefaultTargetUrl(true);  //必须要设置为true,设置url才能生效
    super.onAuthenticationSuccess(request, response, authentication); 
}    

}

MylogoutSuccessHandler.java
/**
* @since 2017-11-25
* @author wwl
*
*/
@Component
public class MylogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler implements LogoutSuccessHandler {

@Autowired
private LogLogServiceImpl logLogServiceImpl;

@DateTimeFormat(pattern="yyyy-MM-dd hh:mm:ss")
Date date = new Date();

@Override  
public void onLogoutSuccess  
  (HttpServletRequest request, HttpServletResponse response, Authentication authentication)   
  throws IOException, ServletException {  
    //退出进行写日志操作
    if(authentication != null){
        User userDetails = (User)authentication.getPrincipal(); 
        LogLog logLog = new LogLog();   //实例化日志bean
        logLog.setUsername(userDetails.getUsername().trim());
        logLog.setRemoteaddr(request.getRemoteAddr().trim());
        logLog.setRemotehost(request.getRemoteHost().trim());
        date = new Date();
        logLog.setLogintime(date);   //加入时间
        logLog.setActions("退出".trim());
        logLogServiceImpl.insertLogLogRecordsService(logLog);
    }else{
        //System.out.println("authentication is null");
    }
   // this.setDefaultTargetUrl("/loginout");    //设置的退出登录的链接
    //this.setAlwaysUseDefaultTargetUrl(true);   //设置为true
    response.sendRedirect("enterLoginPage");
    super.onLogoutSuccess(request, response, authentication);  
}  

}

八、异常处理器ExceptionReslove.java
/**
* 全局统一异常处理器
* @since 2017-12-12
* @author wwl
*
*/
@ControllerAdvice
public class ExceptionReslove {
private static final String ERROR = “error/500”;

private static final String COOKIEURL = "login";


/**
 * 对于CookieTheftException异常处理
 * @param request
 * @param response
 * @param modelAndView
 * @return
 */
@ExceptionHandler({org.springframework.security.web.authentication.rememberme.CookieTheftException.class})
public ModelAndView CookieTheftExceptionHandler(HttpServletRequest request,HttpServletResponse response,
        ModelAndView modelAndView){
    //这里对CookieTheftException特殊处理
    modelAndView.setViewName(this.COOKIEURL);
    return modelAndView;
}

/**
 * 全局异常处理器,可以根据status细处理
 * @param modelAndView
 * @return
 */
@ExceptionHandler({Exception.class})
public ModelAndView ExceptionResloveMethod(HttpServletRequest request,HttpServletResponse response,
        ModelAndView modelAndView){


    modelAndView.addObject("error", "ok");
    modelAndView.setViewName(this.ERROR);
    return modelAndView;
}

}

九、spring security4.x工作流程
Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反转Inversion of Control ,DI:Dependency Injection 依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。
主要有两个重要部分:
1.认证授权
用户登录,会被AuthenticationProcessingFilter拦截,调用AuthenticationManager的实现类,而且AuthenticationManager会调用ProviderManager获取用户验证信息,如果验证通过后会把用户的账户、角色放入spring security内部封装的userDetails的User对象中,最后userDetails会放入SecurityContextHolder安全容器中,用于后面用户访问资源进行权限鉴别。

2.访问授权
访问url时,会通过AbstractSecurityInterceptor拦截器拦截,其中会调用FilterInvocationSecurityMetadataSource的方法来获取被拦截url所需的全部权限,在调用授权管理器AccessDecisionManager,这个授权管理器会通过spring的全局缓存SecurityContextHolder获取用户的权限信息,还会获取被拦截的url和被拦截url所需的全部权限,然后根据所配的策略(有:一票决定,一票否定,少数服从多数等)这里配置的是一票否决方式,如果权限足够,则返回,权限不够则报错并调用权限不足页面。

并且spring security提供了免登陆和日志记录功能(这里的日志记录是改造过的)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值