springboot+springSecurity+mybatis实现权限管理

来源:互联网 发布:美服战网客户端mac 编辑:程序博客网 时间:2024/05/22 01:36

数据库设计



说明:

1.用户可以对应多个角色,角色可以对应多个权限;

2.PermissionAccess对应menu,button,action;


配置文件



pom.xml

<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.iflytek</groupId><artifactId>security_demo</artifactId><version>0.0.1-SNAPSHOT</version><packaging>jar</packaging><name>security_demo</name><description>Demo project for Spring Boot</description><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>1.5.3.RELEASE</version><relativePath/> <!-- lookup parent from repository --></parent><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><java.version>1.8</java.version></properties><repositories><repository><id>opensesame</id><name>Alibaba OpenSource Repsoitory</name><url>http://code.alibabatech.com/mvn/releases/</url><snapshots><enabled>false</enabled></snapshots></repository></repositories><dependencies><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>1.3.0</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><dependency><groupId>org.thymeleaf.extras</groupId><artifactId>thymeleaf-extras-springsecurity4</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.0.18</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.31</version></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>


springboot+mybatis配置


application.properties

spring.datasource.name = securityspring.datasource.url = jdbc:mysql://localhost:3306/security?characterEncoding=utf-8&useSSL=false&serverTimezone=UTCspring.datasource.username = rootspring.datasource.password =spring.datasource.type = com.alibaba.druid.pool.DruidDataSourcespring.datasource.driver-class-name = com.mysql.jdbc.Driverspring.datasource.filters = statspring.datasource.maxActive = 20spring.datasource.initialSize = 1spring.datasource.maxWait = 60000spring.datasource.minIdle = 1spring.datasource.timeBetweenEvictionRunsMillis = 60000spring.datasource.minEvictableIdleTimeMillis = 300000spring.datasource.validationQuery = select 'x'spring.datasource.testWhileIdle = truespring.datasource.testOnBorrow = falsespring.datasource.testOnReturn = falsespring.datasource.poolPreparedStatements = truespring.datasource.maxOpenPreparedStatements = 20server.port=8888



DruidConfig.java

import com.alibaba.druid.pool.DruidDataSource;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Value;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.Primary;import java.sql.SQLException;/** * Created by jjpeng on 2017/5/10. */@Configurationpublic class DruidConfig {    private Logger logger = LoggerFactory.getLogger(getClass());    @Value("${spring.datasource.url}")    private String dbUrl;    @Value("${spring.datasource.username}")    private String username;    @Value("${spring.datasource.password}")    private String password;    @Value("${spring.datasource.driver-class-name}")    private String driverClassName;    @Value("${spring.datasource.initialSize}")    private int initialSize;    @Value("${spring.datasource.minIdle}")    private int minIdle;    @Value("${spring.datasource.maxActive}")    private int maxActive;    @Value("${spring.datasource.maxWait}")    private int maxWait;    @Value("${spring.datasource.timeBetweenEvictionRunsMillis}")    private int timeBetweenEvictionRunsMillis;    @Value("${spring.datasource.minEvictableIdleTimeMillis}")    private int minEvictableIdleTimeMillis;    @Value("${spring.datasource.validationQuery}")    private String validationQuery;    @Value("${spring.datasource.testWhileIdle}")    private boolean testWhileIdle;    @Value("${spring.datasource.testOnBorrow}")    private boolean testOnBorrow;    @Value("${spring.datasource.testOnReturn}")    private boolean testOnReturn;    @Value("${spring.datasource.poolPreparedStatements}")    private boolean poolPreparedStatements;    @Value("${spring.datasource.filters}")    private String filters;    @Bean    @Primary    public DruidDataSource druidDataSource(){        DruidDataSource datasource = new DruidDataSource();        datasource.setUrl(this.dbUrl);        datasource.setUsername(username);        datasource.setPassword(password);        datasource.setDriverClassName(driverClassName);        datasource.setInitialSize(initialSize);        datasource.setMinIdle(minIdle);        datasource.setMaxActive(maxActive);        datasource.setMaxWait(maxWait);        datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);        datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);        datasource.setValidationQuery(validationQuery);        datasource.setTestWhileIdle(testWhileIdle);        datasource.setTestOnBorrow(testOnBorrow);        datasource.setTestOnReturn(testOnReturn);        datasource.setPoolPreparedStatements(poolPreparedStatements);        try {            datasource.setFilters(filters);        } catch (SQLException e) {            logger.error("druid configuration initialization filter", e);        }        return datasource;    }}


MapperScannerConfig.java

import org.mybatis.spring.mapper.MapperScannerConfigurer;import org.springframework.boot.autoconfigure.AutoConfigureAfter;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;/** * Created by admin on 2017/5/11. */@Configuration@AutoConfigureAfter(MybatisConfig.class)public class MapperScannerConfig {    @Bean    public MapperScannerConfigurer mapperScannerConfigurer() {        MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();        mapperScannerConfigurer.setSqlSessionFactoryBeanName("sqlSessionFactory");        mapperScannerConfigurer.setBasePackage("com.iflytek.dao");        return mapperScannerConfigurer;    }}


MybatisConfig.java

import org.apache.ibatis.session.SqlSessionFactory;import org.mybatis.spring.SqlSessionFactoryBean;import org.mybatis.spring.SqlSessionTemplate;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.core.io.support.PathMatchingResourcePatternResolver;import org.springframework.core.io.support.ResourcePatternResolver;import org.springframework.jdbc.datasource.DataSourceTransactionManager;import org.springframework.transaction.PlatformTransactionManager;import org.springframework.transaction.annotation.EnableTransactionManagement;import org.springframework.transaction.annotation.TransactionManagementConfigurer;import javax.sql.DataSource;@Configuration@EnableTransactionManagementpublic class MybatisConfig implements TransactionManagementConfigurer{    @Autowired    private DataSource dataSource;    @Bean(name = "sqlSessionFactory")    public SqlSessionFactory sqlSessionFactoryBean(){        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();        bean.setDataSource(dataSource);        bean.setTypeAliasesPackage("com.iflytek.model");        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();        try {            bean.setMapperLocations(resolver.getResources("classpath:mapper/*"));            return bean.getObject();        } catch (Exception e){            e.printStackTrace();            throw new RuntimeException(e);        }    }    @Bean    public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory){        return new SqlSessionTemplate(sqlSessionFactory);    }    @Bean    @Override    public PlatformTransactionManager annotationDrivenTransactionManager(){        return new DataSourceTransactionManager(dataSource);    }}


Security配置

SecurityConfig.java

import com.iflytek.service.MyFilterSecurityInterceptor;import com.iflytek.service.MyUserDetailsService;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.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;/** * Created by admin on 2017/4/6. */@Configuration@EnableWebSecuritypublic class SecurityConfig extends WebSecurityConfigurerAdapter {    @Autowired    private MyFilterSecurityInterceptor myFilterSecurityInterceptor;    @Bean    UserDetailsService myUserDetailsService(){        return new MyUserDetailsService();    }    @Override    protected void configure(HttpSecurity http) throws Exception {        http.authorizeRequests()                .antMatchers("/hello").permitAll()                .anyRequest().authenticated() //任何请求,登录后可以访问                .and()                .formLogin()                .loginPage("/login")                .failureUrl("/login?error")                .permitAll() //登录页面用户任意访问                .and()                .logout().permitAll(); //注销行为任意访问        http.addFilterBefore(myFilterSecurityInterceptor, FilterSecurityInterceptor.class).csrf().disable();;    }    @Override    public void configure(WebSecurity web) throws Exception {        web.ignoring().antMatchers("/js/**", "/css/**", "/images/**", "/**/favicon.ico");        //防止拦截css,js,image文件    }    @Override    protected void configure(AuthenticationManagerBuilder auth) throws Exception {        auth.userDetailsService(myUserDetailsService());        //登录验证,绑定自定义的UserDetailServiceHolder    }

SpringSecurity登录验证

1.自定义类实现UserDetailsService接口机器loadUserByUserName方法;

2.SpringSecurity的authenticationProcessingFilter拦截器调用AuthenticationManager,UserDetailsService拿到用户信息后,authenticationManager对比用户名密码,如果通过了,则相当于通过了AuthenticationProcessingFilter拦截器,也就是登录验证通过。

3.登录 获取登录用户的用户权限,将用户信息和权限信息保存在SecurityContextHolder中。



myUserDetailsService.java


@Servicepublic class MyUserDetailsService implements UserDetailsService {    @Autowired    private UserDao userDao;    @Autowired    private PermissionDao permissionDao;    @Override    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {        //判断用户是否存在        SYS_User u = userDao.findByAccount(s);        if (u == null){            throw new UsernameNotFoundException(s+"用户名不存在");        }        //根据用户获取权限        List<SYS_Permission> permissions = permissionDao.findByUserId(u.getUserID());        //定义权限集合        List<GrantedAuthority> grantedAuthorities = new ArrayList();                //添加权限到集合中        for (SYS_Permission permission: permissions){            if (permission !=null && permission.getPermissionOperation()==true) {                GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(permission.getPermissionAccess() + "_" + permission.getPermissionAccessValue());                grantedAuthorities.add(grantedAuthority);            }        }        //将已登录的用户信息保存到SecurityContext        org.springframework.security.core.userdetails.User user = new User(u.getAccount(),u.getPassword(),u.getStatus(),true,true, true, grantedAuthorities);        return user;    }}


SpringSecurity资源访问控制


访问资源url

AbstractSecurityInterceptor拦截

调用FilterInvocationSecurityMetadataSource的方法(资源权限关联)来获取被拦截url所需要的全部权限

调用授权管理器AccessDecisionManager(访问决策)

通过全局缓存SecurityContextHolder获取用户的权限信息,decide方法判断用户是否有权限访问该资源


myFilterSecurityInterceptor.java

@Servicepublic class MyFilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {    @Autowired    private FilterInvocationSecurityMetadataSource securityMetadataSource;    @Autowired    public void setMyAccessDecisionManager(MyAccessDecisionManager myAccessDecisionManager){        super.setAccessDecisionManager(myAccessDecisionManager);    }    @Override    public void init(FilterConfig filterConfig) throws ServletException {    }    //登录后,每次访问资源都通过这个拦截器拦截    @Override    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {        FilterInvocation fi = new FilterInvocation(servletRequest,servletResponse,filterChain);        invoke(fi);    }    @Override    public void destroy() {    }    @Override    public Class<?> getSecureObjectClass() {        return FilterInvocation.class;    }    @Override    public SecurityMetadataSource obtainSecurityMetadataSource() {        return this.securityMetadataSource;    }    public void invoke(FilterInvocation fi) throws IOException,ServletException {        //fi里面有一个被拦截的url        //里面调用MyInvocationSecurityMetadataSource的getAttributes(Object object)这个方法获取fi对应的所有权限        //再调用MyAccessDecisionManager的decide方法来校验用户的权限是否足够        InterceptorStatusToken token = super.beforeInvocation(fi);        try{            //执行下一个拦截器            fi.getChain().doFilter(fi.getRequest(),fi.getResponse());        }finally {            super.afterInvocation(token,null);        }    }}



myInvocationSecurityMetadataSource.java

@Servicepublic class MyInvocationSecurityMetadataSourceService implements FilterInvocationSecurityMetadataSource {    @Autowired    private PermissionDao permissionDao;    @Autowired    private MenuDao menuDao;    @Autowired    private ButtonDao buttonDao;    private HashMap<String, Collection<ConfigAttribute>> map =null;    //加载权限表中所有权限    public void loadResourceDefine(){        map = new HashMap<>();        Collection<ConfigAttribute> atts;        //获取所有权限并保存        List<SYS_Permission> permissions = permissionDao.findAll();        for(SYS_Permission permission : permissions) {            atts = new ArrayList<>();            ConfigAttribute cfg = new SecurityConfig(permission.getPermissionAccess() + "_" + permission.getPermissionAccessValue());            atts.add(cfg);            //对应的menu或button的url            if (permission.getPermissionAccess().equals("menu")){                SYS_Menu sys_menu = menuDao.findById(permission.getPermissionAccessValue());                map.put(sys_menu.getUrl(),atts);            }else if (permission.getPermissionAccess().equals("button")){                SYS_Button sys_button = buttonDao.findById(permission.getPermissionAccessValue());                map.put(sys_button.getUrl(),atts);            }        }    }    //此方法是为了判定用户请求是否在权限表中,如果在权限表中,则返回给 decide 方法,用来判定用户是否有此权限。如果不在权限表中则放行。    @Override    public Collection<ConfigAttribute> getAttributes(Object o) throws IllegalArgumentException {        if(map ==null) loadResourceDefine();        //object 中包含用户请求的request 信息        HttpServletRequest request = ((FilterInvocation) o).getHttpRequest();        AntPathRequestMatcher matcher;        String resUrl;        Iterator<String> ite = map.keySet().iterator();        while (ite.hasNext()){            resUrl = ite.next();            matcher = new AntPathRequestMatcher(resUrl);            if(matcher.matches(request)) {                return map.get(resUrl);            }        }        return null;    }    @Override    public Collection<ConfigAttribute> getAllConfigAttributes() {        return null;    }    @Override    public boolean supports(Class<?> aClass) {        return true;    }}


myAccessDecisionManager.java


@Servicepublic class MyAccessDecisionManager implements AccessDecisionManager {    // decide 方法是判定是否拥有权限的决策方法,    //authentication 是UserDetailService中循环添加到 GrantedAuthority 对象中的权限信息集合.    //object 包含客户端发起的请求的requset信息,可转换为 HttpServletRequest request = ((FilterInvocation) object).getHttpRequest();    //configAttributes 为MyInvocationSecurityMetadataSource的getAttributes(Object object)这个方法返回的结果,此方法是为了判定用户请求的url 是否在权限表中,如果在权限表中,则返回给 decide 方法,用来判定用户是否有此权限。如果不在权限表中则放行。    @Override    public void decide(Authentication authentication, Object o, Collection<ConfigAttribute> collection) throws AccessDeniedException, InsufficientAuthenticationException {        if (null == collection || collection.size()<=0){            return;        }        ConfigAttribute c;        String needRole;        Iterator<ConfigAttribute> ite = collection.iterator();        while (ite.hasNext()){            c = ite.next();            needRole = c.getAttribute();            for (GrantedAuthority ga : authentication.getAuthorities()){                if (needRole.trim().equals(ga.getAuthority())){                    return;                }            }        }        throw new AccessDeniedException("no right");    }    @Override    public boolean supports(ConfigAttribute configAttribute) {        return true;    }    @Override    public boolean supports(Class<?> aClass) {        return true;    }}


阅读全文
0 0
原创粉丝点击