《spring boot +spring security安全框架》2020

《spring boot +spring security安全框架》

1.简单篇:

|-导入pom 依赖

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

|-配置config

*配置类添加@EnableWebSecurity 注解
*继承 WebSecurityConfigurerAdapter

@EnableWebSecurity    //注解开启Spring Security的功能
public class SecurityConfig extends WebSecurityConfigurerAdapter {}

*设置用户,基于内存

//用户验证
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception{
    //基于内存来存储用户信息
    auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
            .withUser("user").password(new BCryptPasswordEncoder().encode("pwd")).roles("USER").and()          //设置 
            .withUser("admin").password(new BCryptPasswordEncoder().encode("pwd")).roles("USER","ADMIN");
}

*设置http请求权限认证

//用户权限认证
@Override
protected void configure(HttpSecurity http) throws Exception {
    http
            //这是认证的请求
            .authorizeRequests()
       
            .antMatchers("/","/home","/login","/index","/error").permitAll()  //这些请求 不需要认证
            .antMatchers("/user/**").hasRole("USER")       //user及以下路径,需要USER权限
            .antMatchers("/admin/**").hasRole("ADMIN")
            .and()
            .formLogin().loginPage("/login").defaultSuccessUrl("/")    //登录页面为login,登录成功跳转  "/"
            .and()
            .logout().logoutUrl("/logout").logoutSuccessUrl("/");    //等出路径为logout,登出成功跳转 "/"
}

*忽略 静态资源

/**
* 核心过滤器配置,更多使用ignoring()用来忽略对静态资源的控制
*/
@Override
public void configure(WebSecurity web) throws Exception {
    web
            .ignoring()
            .antMatchers("/image/**");
}

2.添加后门backdoor,例如输入:alex/任意密码就可以通过验证

|- 新建类SecurityProvider类实现AuthenticationProvider接口
|- 实现authenticate()和supports()方法

/**
* 自定义验证类,可以用作backdoor,例如输入:alex/任意密码就可以通过验证
*/
@Component
public class SecurityProvide implements AuthenticationProvider {
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        String name = authentication.getName();
        String password = authentication.getCredentials().toString();

        //利用alex用户名登录,不管密码是什么都可以,伪装成admin用户
        if (name.equals("alex")) {
            Collection<GrantedAuthority> authorityCollection = new ArrayList<>();
            authorityCollection.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
            authorityCollection.add(new SimpleGrantedAuthority("ROLE_USER"));
            return new UsernamePasswordAuthenticationToken(
                    "admin", password, authorityCollection);
        } else {
            return null;
        }
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return authentication.equals(
                UsernamePasswordAuthenticationToken.class);
    }
}

|-添加自定义的认证/验证方式

//用户验证
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception{
    //基于内存来存储用户信息
    auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
            .withUser("user").password(new BCryptPasswordEncoder().encode("pwd")).roles("USER").and()
            .withUser("admin").password(new BCryptPasswordEncoder().encode("pwd")).roles("USER","ADMIN");
    auth.authenticationProvider(securityProvide);
}

|-添加自定义参数名myusername和mypassword

//用户权限认证
@Override
protected void configure(HttpSecurity http) throws Exception {
    http
            //这是认证的请求
            .authorizeRequests()
            //这是不需要认证的请求
            .antMatchers("/","/home","/login","/index","/error").permitAll()
            .antMatchers("/user/**").hasRole("USER")
            .antMatchers("/admin/**").hasRole("ADMIN")
            .and()
            .formLogin().loginPage("/login").defaultSuccessUrl("/")
            .usernameParameter("myusername").passwordParameter("mypassword")
            .and()
            .logout().logoutUrl("/logout").logoutSuccessUrl("/");
}

3.连接数据库user信息

|- entity中user对象实现 UserDetails 接口,并实现其下 7 个方法

         *getAuthorities()
/**
* 从数据库中取出roles字符串后,进行分解,构成一个GrantedAuthority权限集合的List返回
*/
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
    List<GrantedAuthority> authorities =new ArrayList<>();
    String[] split = roles.split(",");
    for (String s: split) {
        authorities.add(new SimpleGrantedAuthority(s));  //添加权限
    }
    return authorities;
}
                *获得 username 与 password 用户信息
@Override
public String getPassword() {return password;}

@Override
public String getUsername() {return username;}
                *其余四个方法 都是以  true 返回

|-service层实现类实现 UserDetailsService 接口,并实现 loadUserByUsername 方法

//查询数据库 用户信息 进行验证
 @Override
     public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
          User s1 =userMapper.findByUsername(username);
          if (s1==null) throw new UsernameNotFoundException("数据库中无此用户!");
          return s1;
     }

        |- SecurityConfig 中添加 service 验证方式
       
    @Autowired
    UserServiceImpl userService  //必须注入 实现UserDetailsService接口的实现层
    ...
    auth.userDetailsService(userService).passwordEncoder(new BCryptPasswordEncoder());
    ...

4.添加自定义鉴权管理器+异常处理类+权限元数据获取

|-自定义鉴权管理器MyAccessDecisionManager 实现AccessDecisionManager+@Component

package com.deceen.config;


import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Component;


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


/**
*  鉴权控制器
*/
@Component
public class MyAccessDecisionManager implements AccessDecisionManager {
    //进行权限 验证
    @Override
    public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {


        //从configAttributes中获取访问资源所需要的角色,它来自MySecurityMetadataSource的getAttributes
        Iterator<ConfigAttribute> iterator = configAttributes.iterator();  //这是来自resource中


        while (iterator.hasNext()){
            ConfigAttribute authority = iterator.next();
            String resourceRole = authority.getAttribute();
            if (resourceRole.equals("ROLE_NONE")){
                if(authentication instanceof AnonymousAuthenticationToken){
                    throw new BadCredentialsException("用户未登录");
                }
                return;
            }

            //用户携带的  权限   ----->   Authentication authentication,authentication.getAuthorities().getAuthority();
            //资源中自定义的   权限   ----->   Collection<ConfigAttribute> configAttributes.getAttribute()

            Iterator<? extends GrantedAuthority> iterator1 = authentication.getAuthorities().iterator();  //这是用户身上携带的角色
            while (iterator1.hasNext()){
                String userRole = iterator1.next().getAuthority();
                if (userRole.equals("ROLE_ADMIN")){
                    return;
                }else if(userRole.equals(resourceRole)){
                    return;
                }
            }
        }
        throw new AccessDeniedException("你没有访问:"+object+"权限");
    }


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


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

|-权限元数据获取 实现 FilterInvocationSecurityMetadataSource+@Component

package com.deceen.config;


import com.deceen.entity.Resource;
import com.deceen.service.ResourceService;
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.stereotype.Component;
import org.springframework.util.AntPathMatcher;


import java.util.Collection;
import java.util.List;


/**
* 用于鉴权中,获取的元数据,resource中对应的  访问路径,权限
*/
@Component
public class MySecurityMetadataSource implements FilterInvocationSecurityMetadataSource {


    @Autowired
    private ResourceService resourceService;
    //本方法返回访问资源所需的角色集合
    AntPathMatcher antPathMatcher = new AntPathMatcher();




    //获取访问路径所对应的权限
    @Override
    public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
        //从object中得到需要访问的资源,即网址
        String url = ((FilterInvocation) object).getRequestUrl();
        List<Resource> all = resourceService.findAll();
        for (Resource resource:all) {
            if (antPathMatcher.match(resource.getUrl(),url)&& resource.getArrayRoles().length>0){
                return SecurityConfig.createList(resource.getArrayRoles());
            }
        }
        return SecurityConfig.createList("ROLE_NONE");
    }


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


    @Override
    public boolean supports(Class<?> clazz) {
        return FilterInvocation.class.isAssignableFrom(clazz);
    }
}

|-权限异常处理类 实现AccessDeniedHandler+@Component

package com.deceen.config;


import com.deceen.entity.vo.JsonResult;
import com.deceen.utils.ObjectMapperUtil;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;


/**
*  验证异常  处理类
*/
@Component
public class MyAccessDeniedHandler implements AccessDeniedHandler {
    @Override
    public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
        httpServletResponse.setStatus(HttpServletResponse.SC_FORBIDDEN);
        httpServletResponse.setContentType("application/json,charset=UTF-8");
        PrintWriter writer = httpServletResponse.getWriter();
        JsonResult info = new JsonResult(500,e.getMessage(),null);
        writer.write(ObjectMapperUtil.toJson(info));
        writer.flush();
        writer.close();
    }
}

|-修改配置类里的 用户权限认证方法

//用户权限认证
@Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                //这是认证的请求
                .authorizeRequests()
//                //这是不需要认证的请求
//                .antMatchers("/","/home","/login","/index","/error").permitAll()
//                .antMatchers("/user/**").hasRole("USER")
//                .antMatchers("/admin/**").hasRole("ADMIN")

                //添加自定义 权限拦截器 FilterSecurityInterceptor 识别
                .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
                    @Override
                    public <O extends FilterSecurityInterceptor> O postProcess(O object) {
                        object.setSecurityMetadataSource(mySecurityMetadataSource);
                        object.setAccessDecisionManager(myAccessDecisionManager);
                        return object;
                    }
                })
                .and()
                .formLogin().loginPage("/login_p").loginProcessingUrl("/login").permitAll()
                //1.自定义参数名称,与login.html中的参数对应
                .usernameParameter("myusername").passwordParameter("mypassword")
                .and()
                .logout().logoutUrl("/logout").logoutSuccessUrl("/login").permitAll()
                .and()
                .csrf().disable()
                .exceptionHandling().accessDeniedHandler(myAccessDeniedHandler);
    }

5.几种获得当前请求中用户的数据

*在Controller层,通过@AuthenticationPrincipal Principal principal(注解可省略)

@GetMapping("/user")
public String user(@AuthenticationPrincipal Principal principal, Model model) {
    model.addAttribute("username",principal.getName());
    return "user/user";
}

*在Controller层,通过 HttpServletRequest

@RequestMapping(value = "/username", method = RequestMethod.GET)
@ResponseBody
public String name(HttpServletRequest request) {
    Principal principal = request.getUserPrincipal();
    return principal.getName();
}

*在任何地方,通过注入 IAuthenticationFacade

@Autowired 
private IAuthenticationFacade authenticationFacade;

@RequestMapping(value = "/username", method = RequestMethod.GET)
@ResponseBody
public String nameSimpe() {
    Authentication authentication = authenticationFacade.getAuthentication();
    return authentication.getName();
}

*在任何地方,通过SecurityContextHolder中的.getContext().getAuthentication()

@GetMapping("/admin")
public String admin( Model model) {
    Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    String username=null;
    if (principal instanceof UserDetails) {
        username= ((UserDetails) principal).getUsername();
    }else if (principal instanceof Principal){
        username= ((Principal) principal).getName();
    }

    model.addAttribute("username",username);
    return "admin/admin";

}

转载:https://zhuanlan.zhihu.com/p/47224331

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值