SSM整合SpringSecurity实现权限管理实例 javaconfig配置方式

SSM项目集成Spring Security 4.X版本(使用spring-security.xml 配置文件方式) 实战目标: Authentication:认证,实现用户认证登录 Authorization:授权,设定用户的资源,访问权限。 阅读详情

1.前言

本文讲述使用javaconfig的方式整合SpringMVC+Mybatis+SpringSecurity实现基于数据库的权限系统,包括对按钮的权限控制。
使用技术: springMVC、springsecurity4、mybatis、ehcache、前端使用dataTables表格、ztree。

2.表结构介绍

image

标准的五张表结构。其中t_resources包含了后台系统的菜单
image
五张表分别为用户表,角色表,资源表,用户角色表,角色资源表。
给用户分配角色,给角色配置权限。形成动态的权限控制。一个用户可以拥有多个角色,一个角色也可以拥有多个资源(即权限)。

3.mavne配置

<properties>  
        <!-- spring版本号 -->  
        <spring.version>4.3.5.RELEASE</spring.version>  
        <springsecurity.version>4.2.1.RELEASE</springsecurity.version>
  </properties> 

  <dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>${spring.version}</version>
    </dependency>
     <dependency>
           <groupId>org.springframework</groupId>
           <artifactId>spring-context-support</artifactId>
           <version>${spring.version}</version>
       </dependency>

    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-web</artifactId>
        <version>${springsecurity.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-config</artifactId>
        <version>${springsecurity.version}</version>
    </dependency>
    <dependency>
         <groupId>org.springframework.security</groupId>
         <artifactId>spring-security-taglibs</artifactId>
         <version>${springsecurity.version}</version>
     </dependency>

      <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.27</version>
        <type>jar</type>
        <scope>compile</scope>
    </dependency>

    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>3.1.0</version>
         <scope>provided</scope>
    </dependency>
    <!-- tomcat jstl -->
    <dependency>
        <groupId>jstl</groupId>
        <artifactId>jstl</artifactId>
        <version>1.2</version>
    </dependency>
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>jsp-api</artifactId>
        <version>2.0</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>commons-logging</groupId>
        <artifactId>commons-logging</artifactId>
        <version>1.2</version>
    </dependency>
     <!-- mybatis核心包 -->  
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.3.0</version>
        </dependency> 
        <!-- mybatis/spring包 -->  
        <dependency>  
            <groupId>org.mybatis</groupId>  
            <artifactId>mybatis-spring</artifactId>  
            <version>1.2.2</version>  
        </dependency>  

          <!--单元测试依赖 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>

        <!--spring单元测试依赖 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${spring.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
           <groupId>com.alibaba</groupId>
           <artifactId>fastjson</artifactId>
           <version>1.2.15</version>
       </dependency>

        <dependency>  
            <groupId>com.github.pagehelper</groupId>  
            <artifactId>pagehelper</artifactId>  
            <version>4.1.4</version>  
        </dependency>  
      <dependency>
        <groupId>commons-dbcp</groupId>
        <artifactId>commons-dbcp</artifactId>
        <version>1.4</version>
    </dependency>

    <!-- ehcache 相关依赖  -->
     <dependency>
         <groupId>net.sf.ehcache</groupId>
         <artifactId>ehcache</artifactId>
         <version>2.7.5</version>
     </dependency>

      <!-- 日志文件管理包 -->  
         <!--ehcache依赖slf4j-->
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-api</artifactId>
                <version>1.7.18</version>
            </dependency>
            <!--ehcache依赖slf4j-->

            <!--slf4j需要log4j-->
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-log4j12</artifactId>
                <version>1.7.18</version>
            </dependency>
            <!--slf4j需要log4j-->

            <!--log4j-->
            <dependency>
                <groupId>org.apache.logging.log4j</groupId>
                <artifactId>log4j-core</artifactId>
                <version>2.5</version>
            </dependency>
            <!--log4j-->

  </dependencies>

4.搭建SpringMVC

首先配置DispatcherServlet:
package com.study.config;

import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;


public class WebProjectConfigInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    /** 
     *  加载驱动应用后端的中间层和数据层组件
     */
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[]{RootConfig.class};
    }
    /** 指定配置类
     *  加载包含web组件的bean,如控制机器、视图解析器以及映射处理器
     */
    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[]{WebConfig.class};
    }

    //将DispatcherServlet 映射到“/”
    @Override
    protected String[] getServletMappings() {
         return new String[] { "/" }; 
    }


}
WebConfig:

主要加载包含WEB的组件bean。如控制器、视图解析器以及处理映射处理器

package com.study.config;

import java.util.List;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;

import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;

@Configuration
@EnableWebMvc
@ComponentScan("com.study.controller")
public class WebConfig extends WebMvcConfigurerAdapter {

    @Bean 
    public ViewResolver viewResolver(){
        InternalResourceViewResolver resolver = new InternalResourceViewResolver();
        resolver.setPrefix("/jsp/");
        resolver.setSuffix(".jsp");
        resolver.setExposeContextBeansAsAttributes(true);
        resolver.setViewClass(JstlView.class);
        return resolver;
    }

    //配置静态资源的处理  使DispatcherServlet对静态资源的请求转发到Servlet容器默认的Servlet上,而不是使用DispatcherServlet本身来处理此类请求
     @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
         configurer.enable();
    }

  /*  @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/css/**").addResourceLocations("classpath:/css");
        registry.addResourceHandler("/js/**").addResourceLocations("classpath:/js");
        registry.addResourceHandler("/img/**").addResourceLocations("classpath:/img");
    }*/


     @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        super.configureMessageConverters(converters);

        FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();

        FastJsonConfig fastJsonConfig = new FastJsonConfig();
        fastJsonConfig.setSerializerFeatures(
                SerializerFeature.PrettyFormat
        );
        fastConverter.setFastJsonConfig(fastJsonConfig);

        converters.add(fastConverter);
    }
}
RootConfig:

主要加载应用中其他bean。这些bean通常是驱动应用后端的中间层和数据层组件。

package com.study.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;

@Configuration
@ComponentScan(basePackages={"com.study"},excludeFilters={@Filter(type=FilterType.ANNOTATION,value=EnableWebMvc.class)})
public class RootConfig {

}

5.配置Spring Security

配置Delegating-FilterProxy
package com.study.config;

import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
/**
 * 配置Delegating-FilterProxy
 * 继承AbstractSecurityWebApplicationInitializer会自动注册DelegatingFilterProxy
 * 等价于xml配置
 * <filter>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>
 * 
 *
 */
public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer{
}
Spring Security 安全配置
package com.study.config;

import javax.annotation.Resource;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.encoding.Md5PasswordEncoder;
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.core.userdetails.UserDetailsService;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import com.study.security.MyFilterSecurityInterceptor;

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

//  @Resource
//  private DataSource dataSource;

    @Resource(name="myUserDetailService")
    private UserDetailsService myUserDetailService;

    @Resource
    private MyFilterSecurityInterceptor myFilterSecurityInterceptor;


//  @Override
//    public AuthenticationManager authenticationManagerBean() throws Exception {  
//      return super.authenticationManagerBean();  
//    }  

    @Override
    public void configure(WebSecurity web)throws Exception {
        // 设置不拦截规则
         web.ignoring().antMatchers("/css/**","/js/**","/img/**","/font-awesome/**");  
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
          http
            .addFilterBefore(myFilterSecurityInterceptor, FilterSecurityInterceptor.class)//在正确的位置添加我们自定义的过滤器  
            .authorizeRequests()
            .anyRequest().authenticated()
//         .and().formLogin().and()
//          .httpBasic();
        // 自定义登录页面
            .and()
            .formLogin().loginPage("/jsp/login.jsp")
            .failureUrl("/jsp/login.jsp?error=1")
            .loginProcessingUrl("/spring_security_check")
            .usernameParameter("username")
            .passwordParameter("password").permitAll().defaultSuccessUrl("/index.do");

          //如果开启了CSRF 退出则需要使用POST访问,可以使用以下方式解决,但并不推荐
          http.logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout"))  
//        登陆成功后跳转的地址,以及删除的cookie名称  
          .logoutSuccessUrl("/jsp/login.jsp?error=logout")  
         .invalidateHttpSession(true);  
    }



    @Override
    protected void configure(AuthenticationManagerBuilder auth)
            throws Exception {

        //启用内存用户存储
        /*auth.inMemoryAuthentication()
        .withUser("user1").password("123456").roles("USER").and()
        .withUser("admin").password("admin").roles("USER","ADMIN");*/
        //
        //给予数据库表认证
        /*auth.jdbcAuthentication().dataSource(dataSource)
        .usersByUsernameQuery("select username,password,enable from t_user where username=?")
        .authoritiesByUsernameQuery("select username,rolename from t_role where username=?");
        */
        //配置自定义的用户服务
        auth.userDetailsService(myUserDetailService).passwordEncoder(new Md5PasswordEncoder());

    }



}

解释:以上代码实现了Spring Security 安全配置。其中@EnableWebSecurity注解将会启用WEB安全功能。
configure(WebSecurity web) 通过重载,配置Spring Security 的Filter链。
configure(HttpSecurity http)通过重载,配置如何通过拦截器保护请求。以上代码配置了自定义的过滤器 、自定义登录页面、和退出功能。其中自定义的过滤器相当于配置了对资源的授权。
configure(AuthenticationManagerBuilder auth)通过重载,配置user_detail服务,其中可以使用基于内存用户存储、基于数据库表进行认证、基于LDAP进行认证,以及自定义的用户服务。以上代码没注释的就是本文基于数据库的自定义权限认证。

自定义认证:

就是在登录时的认证操作交给Spring Security.在此处需要提供给当前登录用户所拥有的权限。即根据用户名查询上面t_resources表中的resKey 拼凑成”ROLE_XXX“ 这样形式的字符串所组成的list ,交给spirngSecurity。

package com.study.security;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.annotation.Resource;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
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 com.study.model.Resources;
import com.study.model.User;
import com.study.service.ResourcesService;
import com.study.service.UserService;

@Component("myUserDetailService")
public class MyUserDetailServiceImpl implements UserDetailsService{

    @Resource
    private UserService userService;

    @Resource
    private ResourcesService resourcesService;
    @Override
    public UserDetails loadUserByUsername(String username)
            throws UsernameNotFoundException {
        User user = userService.findUserByName(username);
        if(user ==null)
            throw new UsernameNotFoundException(username+" not exist!");  
        Set<GrantedAuthority> authSet = new HashSet<GrantedAuthority>();
        Resources resources = new Resources();
        resources.setUsername(username);
        List<Resources> list = resourcesService.loadMenu(resources);
        for (Resources r : list) {
            authSet.add(new SimpleGrantedAuthority("ROLE_" +r.getResKey()));
        }
        return new org.springframework.security.core.userdetails.User(user.getUsername(), 
                user.getPassword(), 
                user.getEnable()==1?true:false, 
                true, 
                true,
                true,
                authSet);
    }

}
自定义过滤器

使访问程序时,检查当前用户是否拥有当前url的权限

package com.study.security;

import java.io.IOException;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
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.SecurityMetadataSource;
import org.springframework.security.access.intercept.AbstractSecurityInterceptor;
import org.springframework.security.access.intercept.InterceptorStatusToken;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.web.FilterInvocation;
import org.springframework.stereotype.Component;



@Component
public class MyFilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {
    @Autowired
    private MySecurityMetadataSource securityMetadataSource;
    @Autowired
    private MyAccessDecisionManager accessDecisionManager;
//  @Resource(name="myAuthenticationManager")
//    private AuthenticationManager authenticationManager;  

    @Resource
    private AuthenticationConfiguration authenticationConfiguration;

//  @Autowired
//  public void setAuthenticationConfiguration(AuthenticationConfiguration authenticationConfiguration) {
//       this.authenticationConfiguration = authenticationConfiguration;
//  }

    @PostConstruct
    public void init() throws Exception{
        super.setAccessDecisionManager(accessDecisionManager);
        super.setAuthenticationManager(authenticationConfiguration.getAuthenticationManager());   
    }

    @Override
    public SecurityMetadataSource obtainSecurityMetadataSource() {
        return this.securityMetadataSource;
    }

    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
        FilterInvocation fi = new FilterInvocation(request, response, chain);
        invoke(fi);
    }

    private void invoke(FilterInvocation fi) throws IOException, ServletException {
        // object为FilterInvocation对象
                  //super.beforeInvocation(fi);源码
        //1.获取请求资源的权限
        //执行Collection<ConfigAttribute> attributes = SecurityMetadataSource.getAttributes(object);
        //2.是否拥有权限
        //this.accessDecisionManager.decide(authenticated, object, attributes);
//      System.err.println(" ---------------  MySecurityFilter invoke--------------- ");
        InterceptorStatusToken token = super.beforeInvocation(fi);
        try {
            fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
        } finally {
            super.afterInvocation(token, null);
        }
    }

    public void init(FilterConfig arg0) throws ServletException {
    }

    public void destroy() {

    }

    @Override
    public Class<? extends Object> getSecureObjectClass() {
        //下面的MyAccessDecisionManager的supports方面必须放回true,否则会提醒类型错误
        return FilterInvocation.class;
    }
}

以上代码涉及到MySecurityMetadataSource和MyAccessDecisionManager

MyAccessDecisionManager
package com.study.security;
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.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Component;
/**
 * <!-- 用户是否拥有所请求资源的权限 -->
 * 
 */
@Component
public class MyAccessDecisionManager implements AccessDecisionManager {

    public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
        if(configAttributes == null) {
            return;
        }
        //所请求的资源拥有的权限(一个资源对多个权限)
        Iterator<ConfigAttribute> iterator = configAttributes.iterator();
        while(iterator.hasNext()) {
            ConfigAttribute configAttribute = iterator.next();
            //访问所请求资源所需要的权限
            String needPermission = configAttribute.getAttribute();
            System.out.println("needPermission is " + needPermission);
            //用户所拥有的权限authentication
            for(GrantedAuthority ga : authentication.getAuthorities()) {
                if(needPermission.equals(ga.getAuthority())) {
                    return;
                }
            }
        }
        //没有权限
        throw new AccessDeniedException(" 没有权限访问或未重新登录! ");
    }

    public boolean supports(ConfigAttribute attribute) {
        // TODO Auto-generated method stub
        return true;
    }

    public boolean supports(Class<?> clazz) {
        // TODO Auto-generated method stub
        return true;
    }

}
MySecurityMetadataSource

需要加载所有资源与权限的关系,即查询t_resources表中所有resKey与resUrl的对应关系。

package com.study.security;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;

import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.stereotype.Component;

import com.study.dao.ResourcesDao;
import com.study.model.Resources;

/**
 * 加载资源与权限的对应关系
 */
@Component
public class MySecurityMetadataSource implements FilterInvocationSecurityMetadataSource {

    @Resource
    private ResourcesDao resourcesDao;


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

    public Collection<ConfigAttribute> getAllConfigAttributes() {
        return null;
    }

    public boolean supports(Class<?> clazz) {
        return true;
    }
    /**
     * @PostConstruct是Java EE 5引入的注解,
     * Spring允许开发者在受管Bean中使用它。当DI容器实例化当前受管Bean时,
     * @PostConstruct注解的方法会被自动触发,从而完成一些初始化工作,
     * 
     * //加载所有资源与权限的关系
     */
    @PostConstruct
    private void loadResourceDefine() {
        if (resourceMap == null) {
            resourceMap = new HashMap<String, Collection<ConfigAttribute>>();
            List<Resources> list = resourcesDao.queryAll(new Resources());
            for (Resources resources : list) {
                Collection<ConfigAttribute> configAttributes = new ArrayList<ConfigAttribute>();
                // 通过资源名称来表示具体的权限 注意:必须"ROLE_"开头
                ConfigAttribute configAttribute = new SecurityConfig("ROLE_" + resources.getResKey());
                configAttributes.add(configAttribute);
                resourceMap.put(resources.getResUrl(), configAttributes);
            }
        }
    }
    //返回所请求资源所需要的权限
    public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
        String requestUrl = ((FilterInvocation) object).getRequestUrl();
    //  System.out.println("requestUrl is " + requestUrl);
        if(resourceMap == null) {
            loadResourceDefine();
        }
        //System.err.println("resourceMap.get(requestUrl); "+resourceMap.get(requestUrl));
        if(requestUrl.indexOf("?")>-1){
            requestUrl=requestUrl.substring(0,requestUrl.indexOf("?"));
        }
        Collection<ConfigAttribute> configAttributes = resourceMap.get(requestUrl);
        return configAttributes;
    }


}

6.配置对按钮的前台显示控制

因为基于了数据库的权限控制,无法通过使用Spring Security的标签来控制对按钮的条件性渲染。不过其实可以通过,来控制显示。但是按钮所对应的Role_XXX就被限定死,而无法修改了。但是在开发阶段,只有按钮所对应操作的url是可以确定的。于是采用自定义标签,使当前的按钮根据按钮的url来确定当前用户是否拥有该url的权限,进而控制按钮的显示。自定义标签如下所示:

AuthorizeTag
package com.study.tag;

import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.jsp.tagext.BodyTagSupport;

import org.springframework.security.core.context.SecurityContextImpl;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.study.model.Resources;
import com.study.service.ResourcesService;
import com.study.util.SpringWiredBean;
/**
 * 自定义标签  用于前台判断按钮权限 
 * @author A
 *
 */
public class AuthorizeTag extends BodyTagSupport {
    private static final long serialVersionUID = 1L;  

    private String buttonUrl;  

    public String getButtonUrl() {
        return buttonUrl;
    }


    public void setButtonUrl(String buttonUrl) {
        this.buttonUrl = buttonUrl;
    }


    @SuppressWarnings("static-access")
    @Override  
    public int doStartTag() {  
        HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();  
        SecurityContextImpl securityContextImpl = (SecurityContextImpl) request
                .getSession().getAttribute("SPRING_SECURITY_CONTEXT");
        //获取当前登录名
        String name = securityContextImpl.getAuthentication().getName();

        //如果数据库里有该链接,并且该用户的权限拥有该链接,则显示 。如果数据库没有该链接则不显示
        ResourcesService resourcesService= (ResourcesService) SpringWiredBean.getInstance().getBeanById("resourcesService");
        List<Resources> queryAll = resourcesService.queryAll();
        boolean flag = true;
        for (Resources resources : queryAll) {
            if(resources.getResUrl().equals(buttonUrl))
                flag = false;
        }
        if(flag) //数据库中没有该链接,直接显示
            return EVAL_BODY_INCLUDE;
        else{
            Resources resources = new Resources();
            resources.setUsername(name);
            resources.setResUrl(buttonUrl);
            List<Resources> resourcesList = resourcesService.loadMenu(resources);
            if(resourcesList.size()>0)  return EVAL_BODY_INCLUDE;//数据库中有该链接,并且该用户拥有该角色,显示
        }
        return this.SKIP_BODY;  //不显示
    }  
}

以上带有用到SpringWiredBean

SpringWiredBean
package com.study.util;


import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.context.support.SpringBeanAutowiringSupport;

public class SpringWiredBean extends SpringBeanAutowiringSupport {

    /**
     * 自动装配注解会让Spring通过类型匹配为beanFactory注入示例
     */
    @Autowired
    private BeanFactory beanFactory;

    private SpringWiredBean() {
    }

    private static SpringWiredBean instance;

    static {
        // 静态块,初始化实例
        instance = new SpringWiredBean();
    }

    /**
     * 实例方法
     * 使用的时候先通过getInstance方法获取实例
     * 
     * @param beanId
     * @return
     */
    public Object getBeanById(String beanId) {
        return beanFactory.getBean(beanId);
    }

    public static SpringWiredBean getInstance() {
        return instance;
    }
}

在WEB-INF下加入authorize.tld

authorize.tld
<?xml version="1.0" encoding="UTF-8" ?>    
<taglib xmlns="http://java.sun.com/xml/ns/javaee"    
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee     
        http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd"    
        version="2.1">    
        <description>    
        <![CDATA[security Tags]]>    
        </description>    
        <tlib-version>1.0</tlib-version>    
        <short-name>security</short-name>    
        <uri>http://www.springsecurity.org/jsp</uri>    
        <tag>    
            <description>    
            <![CDATA[authorize Tag]]>    
            </description>    
            <name>authorize</name>    
            <tag-class>    
                com.study.tag.AuthorizeTag
            </tag-class>    
            <body-content>JSP</body-content>    
            <attribute>    
                <name>buttonUrl</name>    
                <required>false</required>    
                <rtexprvalue>true</rtexprvalue>    
                <type>java.lang.String</type>    
            </attribute>    
        </tag>    
    </taglib>    

则在前端使用时,引入上面定义的标签:

<%@ taglib uri="http://www.springsecurity.org/jsp" prefix="security"%>

然后页面上控制:

 <security:authorize buttonUrl="/role/addRole.do">
     <button type="button" id="btn_search" onclick="$('#addRole').modal();" class="btn btn-info" style="float: right; margin-right: 1;">新增</button>
</security:authorize>

7.配置Mybatis

package com.study.config; 

import java.util.Properties;

import javax.sql.DataSource;

import org.apache.commons.dbcp.BasicDataSource;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.log4j.Logger;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import com.github.pagehelper.PageHelper;

@Configuration
//加载资源文件
@PropertySource({"classpath:jdbc.properties"})
//加上这个注解,使得支持事务
@EnableTransactionManagement
@MapperScan(basePackages = {"com.study.dao"})
public class DataSourceConfig {
    private static final Logger logger = Logger.getLogger(DataSourceConfig.class);
    /*
     * 绑定资源属性
     */
    @Value("${jdbc.driver}")
    private String driverClass;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String userName;
    @Value("${jdbc.password}")
    private String passWord;
    @Value("${maxActive}")
    private int maxActive;
    @Value("${maxIdle}")
    private int maxIdle;
    @Value("${maxWait}")
    private long maxWait;


    /** 
     * 必须加上static 
     */  
    public static PropertySourcesPlaceholderConfigurer placehodlerConfigurer() {
        logger.info("PropertySourcesPlaceholderConfigurer");
        return new PropertySourcesPlaceholderConfigurer();
    }

    @Bean(destroyMethod="close")  
    public BasicDataSource dataSource() {
        logger.info("DataSource");
         BasicDataSource dataSource = new BasicDataSource();
        dataSource.setDriverClassName(driverClass);
        dataSource.setUrl(url);
        dataSource.setUsername(userName);
        dataSource.setPassword(passWord);
        dataSource.setMaxActive(maxActive);
        dataSource.setMaxIdle(maxIdle);
        dataSource.setMaxWait(maxWait);
        return dataSource;
    }

    @Bean
    public PlatformTransactionManager txManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

   @Bean  
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {  
        SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();  
        sessionFactory.setDataSource(dataSource);
        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        sessionFactory.setMapperLocations(resolver.getResources("classpath:mapping/*Mapping.xml"));
        //配置pageHelper 
        sessionFactory.setPlugins(new Interceptor[]{pageHelper()});
        return sessionFactory.getObject();
    }  

    /*@Bean
    public MapperScannerConfigurer scannerConfigurer(){
        MapperScannerConfigurer configurer = new MapperScannerConfigurer();
        configurer.setBasePackage("com.study.dao");
        configurer.setSqlSessionFactoryBeanName("sqlSessionFactory");
        return configurer;
    }*/
    /**
     * mybatis 分页插件配置
     * @return
     */
    @Bean  
    public PageHelper pageHelper() {  
        logger.info("MyBatisConfiguration.pageHelper()");
        PageHelper pageHelper = new PageHelper();  
        Properties p = new Properties();  
        p.setProperty("offsetAsPageNum", "true");  
        p.setProperty("rowBoundsWithCount", "true");  
        p.setProperty("reasonable", "true");  
        pageHelper.setProperties(p);  
        return pageHelper;  
    }

}

以上代码,配置了dataSource、sqlSessionFactory、txManager。还配置了mybatis的分页插件pageHelper。
需要注意的是:上面代码注释了的部分,配置的MapperScannerConfigurer。当写上这段代码后,就会使因为@PropertySource注入的jdbc配置获取的值为null,导致datasource无法配置成功,后来找了一些资料,改成@MapperScan(basePackages = {“com.study.dao”})就可以了。但还在没找到原因,希望哪个大牛看到后可以给我解答,还有因为这个只是一个练习的项目,可能存在很多不妥或者欠缺的地方,各位对这个项目有建议的地方,希望留言或者发送到邮箱
yqj_lovely@163.com,在此先谢谢大家。

再附上CachingConfig

CachingConfig
package com.study.config;

import org.apache.log4j.Logger;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.ehcache.EhCacheCacheManager;
import org.springframework.cache.ehcache.EhCacheManagerFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;

@Configuration  
@EnableCaching//<!-- 启用缓存注解 -->
public class CachingConfig {

     private static final Logger logger = Logger.getLogger(CachingConfig.class);  

    @Bean  
    public EhCacheManagerFactoryBean ehCacheManagerFactoryBean() {  
        EhCacheManagerFactoryBean ehCacheManagerFactoryBean = new EhCacheManagerFactoryBean();  
        ehCacheManagerFactoryBean.setConfigLocation(new ClassPathResource(  
                "ehcache.xml"));  
        return ehCacheManagerFactoryBean;  
    }  

    @Bean  
    public CacheManager cacheManager() {  
        logger.info("EhCacheCacheManager"); 
        EhCacheCacheManager cacheManager = new EhCacheCacheManager();  
        cacheManager.setCacheManager(ehCacheManagerFactoryBean().getObject());  
        return cacheManager;  
    }  
}

8.效果图

image
image
image
从图中可以看到,当勾选相应的权限后,界面上的按钮也跟着消失了。

套了模板,但是界面还是很丑,对前台操作不太擅长,就先这样了,还有对所有的修改操作都没有做,就做了增加和删除(好吧,有点懒的)。

第一次使用时导入mytest.sql
admin 的密码为admin ,因为密码的加密是后面加入的,所以现在库里只有admin可以登录。要想使用其他用户登录,请使用admin用户重新创建用户,
并分配相应的权限。
代码下载:
github 地址 :https://github.com/coder-yqj/springsecuritydemo
csdn 下载http://download.youkuaiyun.com/detail/poorcoder_/9818490

转载请标明出处:http://blog.youkuaiyun.com/poorcoder_/article/details/70231779

SSM框架下的权限管理(附代码) 在网站开发过程中,经常会设置不同类型的账户,如管理员,用户,游客等。对于不同类型的账户,会给予不同的权限。所以就需要用到权限管理。权限管理,一般指根据系统设置的安全规则或者安全策略,用户可以访问而且只能访问自己被授权的资源,不多不少。 在SSM框架下实现权限管理,一般分为如下6步: 1.导jar包 2.web.xml 添加过滤器 3.写springsecurity.xml 核心配置文件 4.web... 阅读详情

相关推荐

图形学基础|屏幕空间反射(SSR)

图形学基础|屏幕空间反射(SSR) 文章目录图形学基础|屏幕空间反射(SSR)一、前言二、反射技术概述2.1 环境贴图反射2.2 IBL反射2.3 平面反射(Planar Reflections)2.4 屏幕空间反射(Screen Space Reflection)2.4.1 SSR的基本原理2.4.2 SSR的优缺点三、SSR(Screen-Space Reflection)实现3.1 Efficient GPU Screen-Space Ray Tracing3.1.1 2D Raymarching v

【黑键】 3万+

SpringBoot集成Security,前后端分离的SecurityConfig配置

前后端分离下保持状态是个问题,但是我这里不涉及分布式,所以用不上JWT,JWT根据项目情况来决定是否使用. Authentication对象会记录用户的状态,所以不用定义一个token,从Authentication中 getAuthorities方法获取用户的状态 如果使用JWT来保持状态的话,就在拦截器上对token进行解码判断就行 先贴上Model代码: pac...

qq_41910048的博客 8488

基于陀螺仪航向反馈的轮式机器人PID直线控制

航向角控制是移动机器人实现稳定直线运动的基础技术,其本质是通过实时感知机体朝向偏差并动态调节驱动输出,以抵消机械不对称、轮径误差等固有扰动。该方法依托陀螺仪测量角速度并积分解算航向角,结合离散化PID算法生成补偿性PWM指令,在嵌入式平台(如STM32)上构建低延迟闭环。相比依赖编码器的速度闭环,它规避了轮径标定与安装偏心等误差源,显著提升低成本平台的工程鲁棒性与部署效率。典型应用场景包括教育机器人直线循迹、AGV基础导航模块开发及IMU-PID融合控制原型验证。

web39explorer的博客 1110

关于Spring Security框架

}// 假设正确的用户名是root,匹配的密码是1234 if(!log . warn("用户名【{}】错误,将不会返回有效的UserDetails(用户详情)");

311

电赛控制组入门到实战:从电路板到PID算法的完整路径

本文详细介绍了电赛控制组从电路板制作到PID算法实战的完整学习路径。涵盖嘉立创电路设计、Keil5开发环境搭建、STM32F103C8T6核心板外设应用,重点解析PID算法在电机速度环与位置环中的调试技巧,并探讨OpenMV视觉模块与主控制器的协同工作,为电赛参赛者提供系统化的实战指南。

oauth7security的博客 747

Linux下Catapro酶动力学预测工具保姆级安装指南(PyTorch+CUDA11.8适配版)

本文提供了Linux系统下Catapro酶动力学预测工具的详细安装指南,特别适配PyTorch+CUDA11.8环境。从环境准备、源码获取到虚拟环境创建、依赖包安装和模型配置,逐步指导用户完成安装流程,并包含常见问题排查和性能优化建议,帮助研究者快速部署这一高效酶动力学参数预测工具。

weixin_29278805的博客 213

SpringSecurity自定义多Provider时提示No AuthenticationProvider found for问题的解决方案与原理(三)

SpringSecurity 5.6.X 自定义多Provider时No AuthenticationProvider found for问题的排查与修复

半斤米粉闯天下的博客 6513

SPSS因子分析实战:从数据清洗到结果解读的完整流程

本文提供了一份详尽的SPSS因子分析实战指南,涵盖从数据清洗、适用性检验(KMO与巴特利特检验)、公因子提取、因子旋转到结果解读与报告撰写的完整流程。通过具体案例,手把手教你如何利用SPSS进行探索性因子分析,实现数据降维并提炼核心公共因子,适用于市场研究、心理学等多变量数据分析场景。

weixin_29055699的博客 332

java菜单管理的实现方式_SSM框架整合SpringSecurity实现权限管理实例 javaconfig配置方式...

1.前言本文讲述使用javaconfig的方式整合SpringMVC+Mybatis+SpringSecurity实现基于数据库的权限系统,包括对按钮的权限控制。 使用技术: springMVC、springsecurity4、mybatis、ehcache、前端使用dataTables表格、ztree。2.表结构介绍标准的五张表结构。其中t_resources包含了后台系统的菜单五张表分别为用户...

weixin_39986178的博客 192

[SystemC]SystemC中的模块和程序

SystemC 中的层次结构是使用类 sc_module 创建的。sc_module 可以直接使用,也可以使用宏 SC_MODULE “隐藏”。上面的示例 SC_MODULE 创建了一个名为 nand2 的 sc_module 类对象。它的输出纯粹是输入值的函数。因此,模型可以使用最简单的 SystemC 进程,即 SC_METHOD。摘要:本节包含一个完整的简单设计,用于演示 SystemC 中模块和流程的使用。同样,重要的是要注意这不是典型的设计风格 - 但它很好而且易于理解。为什么要看模块和流程?

元直的博客 1870

Python 爬虫实战:亚马逊商品评论 + 评分抓取(情感分析数据预处理)

在电子商务领域,用户评论是了解产品口碑和市场反馈的重要渠道。亚马逊作为全球领先的电商平台,拥有海量的商品评论数据。本文将详细介绍如何使用 Python 编写爬虫程序,从亚马逊网站抓取商品评论和评分数据,并进行情感分析的数据预处理。

yansideyucsdn的博客 770

SpringSecurity系列学习之入门篇

版权声明:本文由神州数码云基地团队整理撰写,若转载请注明出处。Spring Security是spring采用AOP思想,基于servlet过滤器实现的安全框架。它提供了完善的认证机制和方法级的授权功能。是一款非常优秀的权限管理框架。学习SpringSecurity,一般都是从前后端不分离架构开始学习,然后学习前后端分离的JWT + SpringSecurity架构,之后再学习SpringSecurity + Oauth2微服务架构。现在大部分项目都是前后端分离的,为什么还需要去看前后端不分离架构下Spri

在数字化道路无限探索 1835

【网络与爬虫 62】小程序爬虫终极攻略:微信支付宝小程序数据采集完全指南

本文深入解析微信和支付宝小程序的爬虫技术,从基础架构分析到高级逆向工程,涵盖WXML/AXML模板解析、API接口拦截、网络流量分析等核心技术。通过实战案例和代码示例,帮助开发者掌握小程序数据采集的完整技术栈。

熵数实验室 3421

新版SpringSecurity配置(SpringBoot>2.7&SpringSecurity>5.7)

在使用或者以上版本时,会提示:在 Spring Security 5.7.0-M2 中,我们弃用了,因为我们鼓励用户转向基于组件的安全配置。所以之前那种通过继承的方式的配置组件是不行的。同时也会遇到很多问题,例如:在向SpringSecurity过滤器链中添加过滤器时(例如:JWT支持,第三方验证),我们需要注入对象等问题。

钟健的博客 1万+

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

spring security中WebSecurityCustomizer的使用

WebSecurityCustomizer` 的主要作用是定制 Spring Security 的过滤器链。它允许你针对不同的请求路径或条件配置不同的安全设置,从而实现更细粒度的控制。

weixin_34278711的博客 1193

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

SpringSecurity 配置与使用(含新 API 替换过时的 WebSecurityConfigurerAdapter)

As we all know,现今主流权限框架有 SpringSecurity、Shiro、SaToken,Shiro在前后端分离时代基本被淘汰,剩下适合大型项目的和 适合中小型项目的SaToken可以选择,SaToken 我也写了文章Springboot 使用 SaToken 进行登录认证、权限管理以及路由规则接口拦截。

泡泡的博客 5723

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

SpringBoot 最新版3.x 集成 OAuth 2.0 实现认证授权服务、第三方应用客户端以及资源服务

Spring Boot 3已经发布一段时间,网上关于Spring Boot 3的资料不是很多,本着对新技术的热情,学习和研究了大量Spring Boot 3新功能和新特性,感兴趣的同学可以参考Spring官方资料全面详细的新功能/新改进介绍Spring版本升级到6.xJDK版本至少17+新特性有很多,本文主要针对OAuth 2.0的集成,如果快速开发自己的认证授权服务、OAuth客户端以及资源服务新建三个服务,分别对应认证授权服务、OAuth客户端以及资源服务。

2301_77463738的博客 4339

SpringSecurity学习

Spring Security,作为Spring框架的重要组件,是一个功能全面且高度可定制的安全框架。它利用Spring的IoC/DI和AOP机制,提供了声明式的安全访问控制,显著减少了开发人员在实现系统安全时所需编写的重复代码。与javaEE的Servlet和EJB规范相比,Spring Security更加贴近企业级应用场景,其配置和功能是WAR/EAR级别独立的,因此更换服务器环境时无需大量重新配置。

2301_76674286的博客 1209

springsecurity整合oauth2

客户端的意思就是比如我是库存系统,我自己没有登陆,但是我想要登陆,那么就需要先在授权服务器进行注册客户端,然后才能拿着授权服务器的登陆,接入到我自己的业务系统,这样我就可以进行单点登陆了。用户拿着用户名密码请求到-》授权中心Oauth2->访问数据库-》返回授权码或者令牌token->认证/校验->授权->访问端点(EndPoint);用户-》请求:带着用户名密码-》认证-》访问数据库-》授权-》访问端点(EndPoint),可以认为是http接口,或者数据;

qq_21277357的博客 2507

spring security+jwt 实现认证授权

目录结构 具体代码 config 配置类 DefaultControllerAdvice.java package com.stu.manage.demo.config; import com.stu.manage.demo.result.Result; import com.stu.manage.demo.result.ResultEnum; import com.stu.manage.demo.result.ResultUtil; import org.springframework.w

IT_cdc的博客 5320

security java的配置_跟我学Spring Security配置(使用Java配置和注解)

Spring Security 借助一系列Servlet Filter 来提供安全性功能,但是借助Spring的小技巧,我们只需要配置一个Filer就可以了,DelegatingFilterProxy是一个特殊的Servlet Filter,它本身所做的工作并不多,只是将工作委托给一个Javax.servlet.Filter 的实现类,这个实现类作为一个bean注册再Spring应用的上下文中。如...

weixin_32051661的博客 403

Spring注解开发——JavaConfig

Spring注解开发-javaConfig开发

菜菜的博客 254

ssm框架整合springSecurity

SSM整合springsecurity过程 前言:先来说一下springsecurity的作用 springsecurity底层实现为一条过滤器链,就是用户请求进来,判断有没有请求的权限,抛出异常,重定向跳转 springsecurity的核心功能: 认证 (我是谁,用户信息验证) 授权 (可以做什么,权限角色) 攻击防护 (防止伪造攻击,csrf) 其核心就是一组过滤器链,项目启动后将会自动配置。最核心的就是 Basic Authentication Filter 用来认证用户的身份,一个在s

weixin_48605326的博客 2177

SSM整合SpringSecurity

SpringMVC中整合SpringSecurity

wujulius的博客 760
上一篇: ActiveMQ介绍及Spring整合实例
下一篇: SpringBoot整合mybatis、shiro、redis实现基于数据库的细粒度动态权限管理系统实例
poorCoder_
博客等级 码龄10年 129粉丝 41原创
评论 19
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值