spring boot security 集成

本文介绍如何在 SpringBoot 中集成 Spring Security 进行权限管理,包括自定义用户服务、实现访问决策管理器和过滤器拦截器等关键组件。

spring boot 中使用注解,bean实例不了,springboot会自动扫描启动文件所在包以及包一下文件注解,否则扫描不到。


spring boot security 集成文件结构:



表设计:

最基本5张表:

用户表:t_user;

资源表:t_resource

角色表:t_role

资源角色表:t_res_role

用户角色表:t_user_role

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.example</groupId>
<artifactId>springboot-security</artifactId>
<version>1</version>
<packaging>war</packaging>


<name>springboot-security</name>
<description>Demo project for Spring Boot</description>


<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.0.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>


<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>


<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-log4j</artifactId>
        <version>1.3.8.RELEASE</version>
    </dependency>
</dependencies>


<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>




</project>


package com.example.security;


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


import org.springframework.beans.factory.annotation.Autowired;
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.Service;


import com.example.dao.RoleDao;
import com.example.dao.UserDao;
@Service
public class CustomUserService  implements UserDetailsService{
@Autowired
    UserDao userDao;
@Autowired
RoleDao roleDao;


    @Override
    public UserDetails loadUserByUsername(String username) { 


        Map<String,Object> user = userDao.queryByUserName(username);
        if(user == null){
            throw new UsernameNotFoundException("用户名不存在");
        }
        List<SimpleGrantedAuthority> authorities = new ArrayList<>();
        //用于添加用户的权限。只要把用户权限添加到authorities 就万事大吉。
     List<Map<String,Object>>roles=   roleDao.queryRoleByUId(user.get("id")+"");
        for(Map<String,Object> role:roles)
        {
            authorities.add(new SimpleGrantedAuthority(role.get("rolId")+""));
        }
        return new org.springframework.security.core.userdetails.User(user.get("name")+"",
                user.get("pwd")+"", authorities);
    }


public UserDao getUserDao() {
return userDao;
}


public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}


public RoleDao getRoleDao() {
return roleDao;
}


public void setRoleDao(RoleDao roleDao) {
this.roleDao = roleDao;
}
    

}



package com.example.security;


import java.util.Collection;
import java.util.Iterator;


import org.springframework.security.access.AccessDecisionManager;
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.Service;
@Service
public class MyAccessDecisionManager implements AccessDecisionManager{


@Override
public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes)
throws org.springframework.security.access.AccessDeniedException, InsufficientAuthenticationException {
if(null== configAttributes || configAttributes.size() <=0) {
            return;
        }
        ConfigAttribute c;
        String needRole;
        for(Iterator<ConfigAttribute> iter = configAttributes.iterator(); iter.hasNext(); ) {
            c = iter.next();
            needRole = c.getAttribute();
            for(GrantedAuthority ga : authentication.getAuthorities()) {//authentication 为在注释1 中循环添加到 GrantedAuthority 对象中的权限信息集合
                if(needRole.trim().equals(ga.getAuthority())) {
                    return;
                }
            }
        }
throw new  org.springframework.security.access.AccessDeniedException("no right");

}


@Override
public boolean supports(ConfigAttribute arg0) {
return true;
}


@Override
public boolean supports(Class<?> arg0) {
return true;
}


}


package com.example.security;


import java.io.IOException;


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.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.stereotype.Service;


@Service
public 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 request, ServletResponse response, FilterChain chain) throws IOException, ServletException {


        FilterInvocation fi = new FilterInvocation(request, response, chain);
        invoke(fi);
    }




    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);
        }
    }


    @Override
    public void destroy() {


    }


    @Override
    public Class<?> getSecureObjectClass() {
        return FilterInvocation.class;
    }


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

package com.example.security;


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 javax.servlet.http.HttpServletRequest;


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.ConfigAttribute;
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.stereotype.Service;


import com.example.dao.ResourceDao;
import com.example.dao.RoleDao;


@Service
public class MyInvocationSecurityMetadataSourceService  implements
        FilterInvocationSecurityMetadataSource {


    @Autowired
    private ResourceDao resourceDao;
    @Autowired
    private RoleDao roleDao;


    private HashMap<String, Collection<ConfigAttribute>> map =null;


    /**
     * 加载权限表中所有权限
     */
    public void loadResourceDefine(){
        map = new HashMap<>();
        Collection<ConfigAttribute> array;
        ConfigAttribute cfg;
        List<Map<String,Object>> permissions = resourceDao.queryAll();
        for(Map<String,Object> permission : permissions) {
            array = new ArrayList<>();
            List<Map<String,Object>>roles=roleDao.queryRoleByResId(permission.get("resId")+"");
            for(Map<String,Object>role : roles){
                cfg = new org.springframework.security.access.SecurityConfig(role.get("rolId")+"");
                array.add(cfg);
            }
        
            //此处只添加了用户的名字,其实还可以添加更多权限的信息,例如请求方法到ConfigAttribute的集合中去。此处添加的信息将会作为MyAccessDecisionManager类的decide的第三个参数。
           
            //用权限的getUrl() 作为map的key,用ConfigAttribute的集合作为 value,
            map.put(permission.get("resUrl")+"", array);
        }


    }


//此方法是为了判定用户请求的url 是否在权限表中,如果在权限表中,则返回给 decide 方法,用来判定用户是否有此权限。如果不在权限表中则放行。
    @Override
    public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
        if(map ==null) loadResourceDefine();
        //object 中包含用户请求的request 信息


        HttpServletRequest request = ((FilterInvocation) object).getHttpRequest();
        AntPathRequestMatcher matcher;
        String resUrl;
        for(Iterator<String> iter = map.keySet().iterator(); iter.hasNext(); ) {
            resUrl = "/"+iter.next();
            matcher=new AntPathRequestMatcher(resUrl);
           String url=request.getRequestURI();
            if(url.equals(resUrl)) {
                return map.get(resUrl);
            }
        }
        return null;
    }


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


    @Override
    public boolean supports(Class<?> clazz) {
        return true;
    }

}


package com.example.security;


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.core.userdetails.UserDetailsService;


@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {


    @Bean
    UserDetailsService customUserService(){ //注册UserDetailsService 的bean
        return new CustomUserService();
    }
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(customUserService()); //user Details Service验证


    }
    @Override
    protected void configure(HttpSecurity http) throws Exception {
            http.authorizeRequests()
                .anyRequest().authenticated() //任何请求,登录后可以访问
                .and()
                .formLogin()
              //  .loginPage("/login")//是否自定义登入页面   如果是自定义登入,登入页面中的action执行login,账户和密码使//username和password
                .failureUrl("/login?error")
                .permitAll() //登录页面用户任意访问
                .and()
                .logout().permitAll(); //注销行为任意访问
        http.sessionManagement().maximumSessions(1).expiredUrl("/login");//是否限制用户人数




    }

}


package com.example.security;




import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;






/**
 * @author 王峰
 * @Time:2017年9月29日 上午10:35:18
 * @version 1.0  
 * Function: TODO
 * 
 * 密码处理
 */
@Service
public class Md5PasswordEncoderService    implements PasswordEncoder {

@Override
public String encode(CharSequence rawPassword) {
// TODO Auto-generated method stub
return null;
}


@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {

return true;
}





}


<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" 
      xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<form name="logonform" method="post" id="loginForm" th:action="@{/login}">
<input type="text" class="usernameInput" name="username" id="j_username" tabindex="1" value=""  title="请输入用户名">
<input type="password" class="passwordInput" name="password" id="j_password" tabindex="2"  value="" title="请输入密码">
<input type="submit">
</form>
</body>

</html>


application.properties

spring.datasource.url=jdbc:mysql://localhost:3306/demo
spring.datasource.username=root
spring.datasource.password=admin
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
logging.level.com.com.example.dao=debug
logging.config=classpath:logging-config.xml
server.port=8081

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值