springboot整合spring-security

来源:互联网 发布:适合初中生看的编程书 编辑:程序博客网 时间:2024/05/17 18:01

前言

前几天学习了spring的权限管理框架spring-security(项目需要),然后自己搭建了一个和数据库结合的权限管理框架,具体如下

POM依赖

因为现在项目基本都用的是maven所以我就不废话了,主要的依赖如下:

<dependency>            <groupId>org.mybatis.spring.boot</groupId>            <artifactId>mybatis-spring-boot-starter</artifactId>            <version>1.3.1</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.springframework.boot</groupId>            <artifactId>spring-boot-starter-web</artifactId>        </dependency>        <dependency>            <groupId>mysql</groupId>            <artifactId>mysql-connector-java</artifactId>            <scope>runtime</scope>        </dependency>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-test</artifactId>            <scope>test</scope>        </dependency>        <dependency>            <groupId>org.springframework.security</groupId>            <artifactId>spring-security-test</artifactId>            <scope>test</scope>        </dependency>

具体的用户对象

我创建的对象有SRole、SysUser、SysResource,创建了5张表(用户和角色中间表和角色和资源的一张中间表,其实spring-security不管你多少张表你只要把你登陆的用户是什么角色(也可以说有什么权限),每个资源应该被那些角色(权限)所访问就行了(具体我会在下一章说到))
SRole角色:

public class SRole {    private Integer id;    private String  name;}

SysUser角色:
public class SysUser {
private Integer id;
private String num;
private String password;
private List roles; // 所拥有的角色
}
SysResource

public class SysResource {    private Integer id;    private String  resourcename;}

好了以上就是一些简单的创建和配置,接下来的才是重点,spring-security可以根据很多方式来实现权限什么内存啦,配置啦,数据库啦,修改源码什么的(这个是真的牛逼),这里我只说数据库.

配置登陆和退出的config

在认证的时候肯定是需要登陆页面、退出页面、那些请求拦截,那些请求不拦截等等,这些是通过继承WebSecurityConfigurerAdapter来实现的我的代码如下所示:

import org.springframework.beans.factory.annotation.Autowired;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 com.example.service.security.CustomUserDetailsService;@Configuration@EnableWebSecuritypublic class WebSecurityConfig extends WebSecurityConfigurerAdapter{    @Autowired    private CustomUserDetailsService cds;    @Override    protected void configure(AuthenticationManagerBuilder auth) throws Exception {        auth.userDetailsService(cds);    }    @Override    protected void configure(HttpSecurity http) throws Exception {        http            .authorizeRequests()            .antMatchers("/","/index").permitAll() //访问 '/' 和 '/index' 无需登录认证权限            .anyRequest().authenticated()//其他所有资源都需要认证,登陆后访问            .and()            .formLogin()            .loginPage("/login") //指定登陆页面是login            .defaultSuccessUrl("/index")            .permitAll()            .and()            .logout()            .logoutUrl("/loginout")            .permitAll();    }}

这时候你脑子里肯定有一个疑问:WIF?这配置什么登陆啊登出啊我懂了那么一丢丢,这个飞来的CustomUserDetailsService 是干啥的?我在我写的

CustomUserDetailsService

嗯,是正确的时间贴出来CustomUserDetailsService的类了

import java.util.ArrayList;import java.util.List;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.security.core.GrantedAuthority;import org.springframework.security.core.authority.SimpleGrantedAuthority;import org.springframework.security.core.userdetails.UserDetails;import org.springframework.security.core.userdetails.UserDetailsService;import org.springframework.security.core.userdetails.UsernameNotFoundException;import org.springframework.stereotype.Service;import com.example.dao.SysUserDao;import com.example.domain.SRole;import com.example.domain.SysUser;@Servicepublic class CustomUserDetailsService implements UserDetailsService{    @Autowired    private SysUserDao sud;    @Override    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {        SysUser user = sud.findByName(username);        if(user == null) throw new UsernameNotFoundException(username);        List<GrantedAuthority>  authorities = new ArrayList<>();        for(SRole role : user.getRoles()) {            authorities.add(new SimpleGrantedAuthority(role.getName()));        }        return new org.springframework.security.core.userdetails.User(user.getNum(),user.getPassword(),authorities);    }}

OK接下来说说我们还需要什么类吧,spring-security是通过拦截器来实现权限啊什么资源的保护的,那正常步骤我们需要自定义一个拦截器,嘿嘿。。。。有没有那么一点点颤抖的感觉。直接上代码吧骚年。。

Filter

import java.io.IOException;

import javax.annotation.PostConstruct;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.SecurityMetadataSource;
import org.springframework.security.access.intercept.AbstractSecurityInterceptor;
import org.springframework.security.access.intercept.InterceptorStatusToken;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.web.FilterInvocation;
import org.springframework.stereotype.Service;

import com.example.mysecurity.MyAccessDecisionManager;
import com.example.mysecurity.MyInvocationSecurityMetadataSource;

@Service
public class MySecurityFilter extends AbstractSecurityInterceptor implements Filter{

@Autowiredprivate MyInvocationSecurityMetadataSource mySecurityMetadataSource;@Autowiredprivate MyAccessDecisionManager myAccessDecisionManager;@Autowiredprivate AuthenticationManager authenticationManager;@PostConstruct  public void init(){      super.setAuthenticationManager(authenticationManager);      super.setAccessDecisionManager(myAccessDecisionManager);  }  @Overridepublic void destroy() {     System.out.println("filter===========================end");  }@Overridepublic 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{      System.out.println("filter..........................");      InterceptorStatusToken  token = super.beforeInvocation(fi);      try{          fi.getChain().doFilter(fi.getRequest(), fi.getResponse());      }finally{          super.afterInvocation(token, null);      }  }  @Overridepublic void init(FilterConfig arg0) throws ServletException {     System.out.println("filter===========================");  }@Overridepublic Class<?> getSecureObjectClass() {    return FilterInvocation.class;}@Overridepublic SecurityMetadataSource obtainSecurityMetadataSource() {    return this.mySecurityMetadataSource;}

}
OK不知道你看到这段代码的时候有没有蒙逼,但是我照这网上其他例子配的时候是真的蒙逼…….完全不知道MyAccessDecisionManager,MyInvocationSecurityMetadataSource是做什么的配它们有什么用。那个我先把这两个类的代码给大家贴出来,慢慢say say.

import java.util.Collection;import java.util.Iterator;import org.springframework.security.access.AccessDecisionManager;import org.springframework.security.access.AccessDeniedException;import org.springframework.security.access.ConfigAttribute;import org.springframework.security.access.SecurityConfig;import org.springframework.security.authentication.InsufficientAuthenticationException;import org.springframework.security.core.Authentication;import org.springframework.security.core.GrantedAuthority;import org.springframework.security.core.userdetails.UserDetails;import org.springframework.stereotype.Service;@Servicepublic class MyAccessDecisionManager implements AccessDecisionManager{    /**     * 验证用户是否有权访问权限     * SecurityContextHolder存储当前与应用程序交互的委托人的细节,Spring Security使用一个Authentication来表示这个信息     */    @Override    public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes)            throws AccessDeniedException, InsufficientAuthenticationException {        /*允许访问没有设置权限的资源*/        if(configAttributes == null) {            return;        }        //configAttributes是从MyInvocationSecurityMetadataSource的getAttributes方法得到的url所有权限拥有者        Iterator<ConfigAttribute> ite = configAttributes.iterator();        while( ite.hasNext()){              ConfigAttribute ca = ite.next();              String needRole = ((SecurityConfig)ca).getAttribute();             //ga 为用户所被赋予的权限。 needRole 为访问相应的资源应该具有的权限。</span>              for( GrantedAuthority ga: authentication.getAuthorities()){                  //如果url所对应的角色权限中包含当前用户的角色则投赞成票                if(needRole.trim().equals(ga.getAuthority().trim())){                      return;                  }              }          }        System.out.println("权限不足");        throw new AccessDeniedException("权限不足");     }    @Override    public boolean supports(ConfigAttribute attribute) {        return true;    }    @Override    public boolean supports(Class<?> clazz) {        return true;    }}

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.annotation.PostConstruct;

import org.springframework.beans.factory.annotation.Autowired;
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.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.stereotype.Service;

import com.example.dao.SResourceVODao;
import com.example.dao.SRoleVODao;

@Service
public class MyInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {

private static Map<String, Collection<ConfigAttribute>> resourceMap = new HashMap<String, Collection<ConfigAttribute>>();@Autowiredprivate SResourceVODao sResourceVODao; @Autowiredprivate SRoleVODao sRoleVODao;/** * 被@PostConstruct修饰的方法会在服务器加载Servle的时候运行, * 并且只会被服务器执行一次。 * PostConstruct在构造函数之后执行,init()方法之前执行 */@PostConstruct private void loadResourceDefine() {    //List<Map<String,String>> list =sRoleVODao.findAll();    List<String> roles = sRoleVODao.findAll(); //得到所有角色    resourceMap = new HashMap<String, Collection<ConfigAttribute>>();    for (String auth : roles) {         ConfigAttribute ca = new SecurityConfig(auth);         List<String> resources = sResourceVODao.findByRoleName(auth); //通过角色查找到所对应的资源        for(String resource : resources) {            //以资源为key将角色放到集合里面            if (resourceMap.containsKey(resource)) {                  Collection<ConfigAttribute> value = resourceMap.get(resource);                  value.add(ca);              } else {                  Collection<ConfigAttribute> atts = new ArrayList<ConfigAttribute>();                  atts.add(ca);                  resourceMap.put(resource, atts);              }         }    }}//得到url对应的所有权限拥有者@Overridepublic Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {    FilterInvocation filterInvocation = (FilterInvocation) object;     if (resourceMap == null) {          loadResourceDefine();      }    Iterator<String> ite = resourceMap.keySet().iterator();      while (ite.hasNext()) {          String resURL = ite.next();          RequestMatcher requestMatcher = new AntPathRequestMatcher(resURL);          if(requestMatcher.matches(filterInvocation.getHttpRequest())) {              return resourceMap.get(resURL);          }      }      return null;}@Overridepublic Collection<ConfigAttribute> getAllConfigAttributes() {     return new ArrayList<ConfigAttribute>(); }@Overridepublic boolean supports(Class<?> clazz) {    return true;}

}
OK配上这些东西剩下的就只剩下建数据库表了,大家自己动动手吧,我赖给大家讲解一下他们之间的关系(本来还想画图呢但是我的word不能用了)。
MysecurityFilter过滤器会从MyInvocationSecurityMetadataSource拿到所有资源(数据库里面的,就是那个getAttributes方法)和每个资源所对应的权限,然后MyAccessDecisionManager拿着这些资源和用户的那些资源权限进行一波对比就是那个decide()方法来判断是否授权。

结语

OK,差不多就可以使用了,至于源码什么的,嗯可能下一章会讲吧,也就只讲一点点

原创粉丝点击