Spring Security 学习总结

这里使用的是jdk1.8,spring boot 、spring security、mybatis、mysql,使用spring security 的目的是实现认证和鉴权。

 

1、环境搭建

1.1引入spring  security 相关依赖

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

这里spring   security 的版本由spring boot 决定,如果按照自己的版本来配置如下:

<properties>
    <spring-security.version>5.2.1.RELEASE</spring-security.version>
</dependencies>

1.2 spring security 的基本配置

创建一个spring security 的核心配置类并继承WebSecurityConfigurerAdapter, 加上@Configuration、@EnableWebSecurity注解,表示使用spring security 的过滤器链springSecurityFilterChain,

2、Spring Security 常用到的对象/接口

2.1 SecurityContextHolder   保存着安全信息,能获得 SecurityContext

2.2 Authentication 对象

不能直接创建这个对象,这个对象有认证等方面信息,如果想获取这个对象,如下方式:

Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();

if (principal instanceof UserDetails) {
String username = ((UserDetails)principal).getUsername();
} else {
String username = principal.toString();
}

2.3 UserDetailsService    基于数据库进行用户认证,全靠它获取登录的用户信息

UserDetailsService     是spring security 的接口
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;

一旦用户认证成功,就会利用UserDetails   构建Authentication 对象。

2.4 AuthenticationManager 用户认证全靠它,就是认证管理

2.5 AuthenticationProvider  如果自己定制一个认证流程,要实现这个接口

2.6 GrantedAuthority 用户的全部权限 

2.7 AbstractSecurityInterceptor 处理请求  必须实现   

2.8 SecurityContextPersistenceFilter 拦截请求生成 security context

2.9 AccessDecisionManager  对访问的请求进行授权的接口  必须实现

2.10 SecurityMetadataSource  认证对象后  获取权限资源接口 必须实现

2.11 security object model

 

2.12 PasswordEncoder 自定义密码加密方式,需要实现的接口

3、spring security  认证

3.1 认证流程

1、用户登录,将用户输入的用户名、密码信息被绑定到UsernamePasswordAuthenticationToken (实现 Authentication 接口);

2、AuthenticationManager  获得token 用于校验;

3、AuthenticationManager 认证成功,返回Authentication 对象;

4、通过返回的Authentication 对象,使用SecurityContextHolder.getContext().setAuthentication(…​),

构建出 security context。

认证方式:form-base login 和 Basic authentication.

 

4、认证和授权简单例子,基于数据库

4.1、spring security 核心配置

/**
 * 
 */
package com.springbootDemo.config;

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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import com.springbootDemo.service.UserService;
import com.springbootDemo.util.security.MyPasswordEncoder;


/**
 * Spring security 核心配置类 用于认证、授权
 * 
 *
 */
@Configuration
@EnableWebSecurity
public class MySecurityConfig extends WebSecurityConfigurerAdapter {
    
    @Autowired
    private UserService userService;
    @Autowired
    private MyFilterSecurityInterceptor myFilterSecurityInterceptor;
    

    /**
     * security 两种认证登录模式 formLogin 和 http basic   这里采用formLogin 模式
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
          //对所有的请求进行拦截
        http.authorizeRequests()
            .anyRequest().authenticated()
            .and()
            .logout().permitAll()
            .and()
            .formLogin();
        http.addFilterBefore(myFilterSecurityInterceptor, FilterSecurityInterceptor.class);
        //关闭spring security 默认的 csrf认证
        http.csrf().disable();
    }

    /**
     * 对进行登录操作的用户进行认证
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //不加.passwordEncoder(new MyPasswordEncoder())
        //就不是以明文的方式进行匹配,会报错
        //这样,页面提交时候,密码以明文的方式进行匹配。
        //采用BCrypt强哈希加密
        auth.userDetailsService(userService).passwordEncoder(new MyPasswordEncoder());
    }

    /**
     * 全局配置忽略哪些请求资源被认证
     */
    @Override
    public void configure(WebSecurity web) throws Exception {
        //设置静态资源不被拦截
        web.ignoring().antMatchers("/resources/**");
    }
    
    /**
     * 强哈希、盐加密
     * @return
     */
    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

}
4.2、  用户登录认证必须实现UserDetailsService

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        UserDetails userDetails = null;
        UserPO user = null;
        try {
            user=this.findUserByUsername(username);
            if(user == null){
                throw new UsernameNotFoundException(username + " do not exist!");
            } 
            List<RolePO> roleList = roleMapper.findRoleListByUserId(user.getUserId());
            List<GrantedAuthority> grantedAuthorities = new ArrayList<GrantedAuthority>();
            
            //写入用户的角色  ***  切记 由于框架原因 角色名称要以 ROLE_ 开头 
            //源码:org.springframework.security.access.expression.SecurityExpressionRoot hasAnyRole()
            if(!roleList.isEmpty()) {
                for (RolePO role : roleList) {
                    SimpleGrantedAuthority grantedAuthority = new SimpleGrantedAuthority(role.getRoleCode());
                    grantedAuthorities.add(grantedAuthority);
                    
                }
            }
            
            userDetails= new User(user.getUsername(), user.getPassword(), true, true, true, true,grantedAuthorities );
        } catch (Exception e) {
            e.printStackTrace();
            userDetails=null;
        }
        return userDetails;
    }


4.3、  实现 FilterInvocationSecurityMetadataSource(它继承了 SecurityMetadataSource)

/**
 * 
 */
package com.springbootDemo.config;

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

import javax.servlet.http.HttpServletRequest;

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.stereotype.Service;

import com.springbootDemo.entity.AuthOperPO;
import com.springbootDemo.mapper.RoleMapper;

/**
 *spring security 权限资源
 */
@Service
public class MySecurityMetadataSourceService implements FilterInvocationSecurityMetadataSource {

    @Autowired
    private RoleMapper roleMapper;
    
    private HashMap<String, Collection<ConfigAttribute>> map =null;

    /**
     * 获取所有权限资源
     */
    public void loadResourceDefine(){
        map = new HashMap<>();
        Collection<ConfigAttribute> configAttributeListAttributeList;
        ConfigAttribute cfg = null ;
        List<AuthOperPO> permissions = roleMapper.findAuthOperPOList();
        for(AuthOperPO permission : permissions) {
            configAttributeListAttributeList=new ArrayList<ConfigAttribute>();
            cfg = new SecurityConfig(permission.getRoleCode());
            //此处只添加了用户的名字,其实还可以添加更多权限的信息,例如请求方法到ConfigAttribute的集合中去。此处添加的信息将会作为MyAccessDecisionManager类的decide的第三个参数。
            configAttributeListAttributeList.add(cfg);
            //用权限的getUrl() 作为map的key,用ConfigAttribute的集合作为 value,
            map.put(permission.getMenuUrl(), configAttributeListAttributeList);
        }

    }

    @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);
            if(matcher.matches(request)) {
                return map.get(resUrl);
            }
        }
        return null;
    }

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

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

}
4.4、实现授权管理器

/**
 * 
 */
package com.springbootDemo.config;

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.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Service;

/**
 *spring  security 鉴权  决策
 *根据URL资源权限和用户角色,进行鉴权
 */
@Service
public class MyAccessDecisionManager  implements AccessDecisionManager {

    /**
     *  // decide 方法是判定是否拥有权限的决策方法,
  //authentication 是释CustomUserService中循环添加到 GrantedAuthority 对象中的权限信息集合.
  //object 包含客户端发起的请求的requset信息,可转换为 HttpServletRequest request = ((FilterInvocation) object).getHttpRequest();
  //configAttributes 为MyInvocationSecurityMetadataSource的getAttributes(Object object)这个
     */
    @Override
    public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) {

    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())) {//匿名访问用户  角色   ROLE_ANONYMOUS
                return;
            }
        }
    }
    throw new AccessDeniedException("no right");

    }

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

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

}


未完待续.................................

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值