架构篇--springboot2.0.x 集成security,安全架构,预防攻击,认证用户,角色权限分配等等

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/smd2575624555/article/details/82759863

springboot2.0.x 集成security

最近在接触安全模块,对于所涉及到的知识进行梳理总结,以下是我最近梳理的结果,构建思维导图,更直观的展示出所有注意到的模块。

 

上代码:自己撸(项目源码)

https://github.com/shimingda/security.git

项目架构

项目采用springboot2.0.2作为技术架构,需要使用权限角色处理,通过考察选取集成spring security框架。

主要依赖

spring-boot-starter-web
spring-boot-starter-security
spring-boot-starter-aop
spring-boot-starter-jdbc
spring-boot-starter-data-jpa
spring-security-oauth2
org.springframework.security
spring-boot-starter-redis
lombok
fastjson
logback

spring security config

import com.dome.authenticate.AuthenticateProvider;
import com.dome.authenticate.MyCustomUserService;
import com.dome.config.properties.SecurityProperties;
import com.dome.handler.MerryyouAuthenticationfailureHandler;
import com.dome.handler.MerryyouLoginSuccessHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
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.config.core.GrantedAuthorityDefaults;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
import org.springframework.security.web.session.InvalidSessionStrategy;
import org.springframework.security.web.session.SessionInformationExpiredStrategy;

/**
 * 核心配置文件
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private MyCustomUserService userService;

    @Autowired
    private SecurityProperties securityProperties;

    @Autowired
    private SessionInformationExpiredStrategy sessionInformationExpiredStrategy;

    @Autowired
    private MerryyouLoginSuccessHandler myAuthenticationSuccessHandler;

    @Autowired
    private MerryyouAuthenticationfailureHandler myAuthenticationFailHandler;

    @Autowired
    private SessionRegistry sessionRegistry;

    @Autowired
    private InvalidSessionStrategy invalidSessionStrategy;
    @Autowired
    public void globalConfigure(AuthenticationManagerBuilder auth){
        auth.authenticationProvider(authenticateProvider());
    }
    // Remove the ROLE_ prefix
    @Bean
    GrantedAuthorityDefaults grantedAuthorityDefaults() {
        return new GrantedAuthorityDefaults("");
    }
    @Bean
    public AuthenticateProvider authenticateProvider() {
        AuthenticateProvider provider = new AuthenticateProvider();
        provider.setUserDetailsService(userService);
        provider.setPasswordEncoder(new BCryptPasswordEncoder());
        return provider;
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        AuthenticationManager manager = super.authenticationManagerBean();
        return manager;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //权限控制
        http
                .authorizeRequests()
                .antMatchers("/user/save","/session/*").permitAll()
                .antMatchers("/test/role").hasAnyRole("admin")
                .antMatchers("/test/permission").hasRole("super_admin")
                .antMatchers("/user/permission").access("super_admin")
                .anyRequest().authenticated();
        //session管理
        http
                .sessionManagement()
                .invalidSessionStrategy(invalidSessionStrategy)//session失效策略处理
                .maximumSessions(securityProperties.getSession().getMaximumSessions())//最大session并发数量1
                .maxSessionsPreventsLogin(securityProperties.getSession().isMaxSessionsPreventsLogin())//之后的登录踢掉之前的登录
                .expiredSessionStrategy(sessionInformationExpiredStrategy)//并发过期处理
                .sessionRegistry(sessionRegistry)
                ;
        //http缓存
        http
                .requestCache()
                .requestCache(new HttpSessionRequestCache());
        //登录验证配置post验证
        http
                .formLogin()
                .loginProcessingUrl("/user/login2")
                .usernameParameter("username")
                .passwordParameter("password")
                // 自定义的登录验证成功或失败后的去向
                .successHandler(myAuthenticationSuccessHandler)
                // .successHandler(appLoginInSuccessHandler)
                .failureHandler(myAuthenticationFailHandler);
        // 安全退出用户
        http
                .logout()
                .logoutUrl("signOut").permitAll()
                .deleteCookies("")
                .invalidateHttpSession(true)
                .logoutSuccessUrl("/");
        // 禁用csrf防御机制(跨域请求伪造),这么做在测试和开发会比较方便。
        http
                .csrf().disable();
        // token管理
//        http
//                .rememberMe()
//                .tokenValiditySeconds(securityProperties.getRememberMeSeconds())
//                .userDetailsService(userService);
        http.httpBasic();
    }
}

项目集成考察

下面是对项目集成进行考察内容,大家有兴趣可以看一下。如果有人问起,可以说出根据,以免被人问的措手不及。

 

用户验证模块

 

用户验证,主要是登录验证,其中可以集成形式,但是在代码中没有去过多实现,因为暂时没有需要去进行集成。如果后续涉及,在进行添加。

认证核心代码


import com.dome.entity.SysUser;
import com.dome.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Component;

@Component
public class MyCustomUserService implements UserDetailsService {
    /**
     * 登陆验证时,通过username获取用户的所有权限信息
     * 并返回UserDetails放到spring的全局缓存SecurityContextHolder中,以供授权器使用
     */
    @Autowired
    private UserService userService;
    @Override
    public UserDetails loadUserByUsername(String username) {
//            获取当前的用户
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            SysUser user=null;
            if (null!=authentication){
               user= (SysUser) authentication.getPrincipal();
            }
            if(null==user){
                user=userService.getUserByName(username);
                if (null==user) {
                    throw new BadCredentialsException("用户不存在");
                }
            }
            return user;
        }
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
import java.util.Collection;

/**
 * 认证
 */
@Component
public class AuthenticateProvider extends DaoAuthenticationProvider {

    @Autowired
    private PasswordEncoder passwordEncoder;
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        String username = authentication.getName();
        String password = (String) authentication.getCredentials();
        UserDetails user = this.getUserDetailsService().loadUserByUsername(username);

        if (!user.getPassword().equals(passwordEncoder.encode(password))){
            throw new BadCredentialsException("用户名密码不匹配");
        }
        if (user.isEnabled()) {
            throw new BadCredentialsException("用户被禁用");
        }
        Collection<? extends GrantedAuthority> grantedAuthorities = user.getAuthorities();
        return new UsernamePasswordAuthenticationToken(user, password,grantedAuthorities);
    }

    @Override
    protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
        super.additionalAuthenticationChecks(userDetails, authentication);
        if (!userDetails.isEnabled()) {
            throw new DisabledException("账户被禁用,请联系管理员");
        }
    }
}

建议

这部分入手首先去了解用户登录到验证的流程,这种流程梳理网上很多,看一看就能抓住里面的核心是什么,在进行代码编写会轻松很多。

注意

用户信息保密,对于密码要加密处理。加密方式多种,考核适合的进行集成

token根据业务需求进行设置,进行不要太长,如果用户过多需要考虑内存性能问题。

集成OAuth2

项目有需要可以去尝试集成OAuth2,在分享的项目中,已集成github第三方登录。可尝试QQ,微信,微博等内容,需要申请授权,过程耗时,没有去实现。

做这个坑很多,建议先看一下http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html这篇博客,讲的非常好。

请求授权访问时有坑要注意:

 

 

 

 

权限角色处理

会话管理

请求监控

防止暴力访问代码

简单实现功能,达到防止暴力访问即可

import com.dome.redis.CacheUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.web.servlet.server.Session;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
/**
 * 预防暴力访问和记录url访问信息
 *
 */
@Aspect
@Component
public class HttpAspect {

    private final static Logger logger = LoggerFactory.getLogger(HttpAspect.class);
    Map<String,LinkedList<Long>> apiMap=new HashMap<>();
    LinkedList<Long> timeList=null;
    @Pointcut("execution(public * com.dome.controller.*.*(..))")
    public void log() {
        System.out.println(123);
    }
    //todo
    //现在存放在session中,应该放在redis
    @Before("log()")
    public void doBefore(JoinPoint joinPoint) throws Exception {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();

        HttpServletRequest request = attributes.getRequest()
        String sessionId=request.getSession().getId();
        StringBuffer url =request.getRequestURL();
        url.append(request.getMethod()).append(sessionId);

        timeList= CacheUtils.getBean(url.toString(),LinkedList.class);
        if(timeList==null||timeList.isEmpty()){
            timeList=new LinkedList<>();
        }
        Long createTime = System.currentTimeMillis();
        timeList.add(createTime);

        int count=timeList.size();
        if(count>10){
            Long lastTime=timeList.getLast();
            Long last2Time=timeList.get(count-1);
            System.out.println(lastTime);
            System.out.println(last2Time);
            if(lastTime-last2Time<3000){
                Long firstTime=timeList.getFirst();
                System.out.println(firstTime);
                if(lastTime-firstTime<30000){
                    throw new Exception("调用太过频繁");
                }
            }
        }
        logger.info("url:{}访问次数为count={}}", request.getRequestURL(), count);
        //url method ip
        logger.info("利用AOP记录每次请求的有关信息,url={},method={},ip={}", request.getRequestURL(), request.getMethod(), request.getRemoteAddr());
        //类方法 参数
        logger.info("class_method={}, args={}", joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName(), joinPoint.getArgs());
        CacheUtils.saveBean(url.toString(),timeList);
    }

    @After("log()")
    public void doAfter() {

    }

    @AfterReturning(returning = "object", pointcut = "log()")
    public void doAfterReturning(Object object) {

    }

}

异常处理

上代码:自己撸(项目源码)

https://github.com/shimingda/security.git

 

猜你喜欢

转载自blog.csdn.net/smd2575624555/article/details/82759863