springboot前后端分离整合spring security

基于session

1.导入依赖

<dependencies>
        <dependency>
            <groupId>com.alibabagroupId>
            <artifactId>fastjsonartifactId>
            <version>1.2.49version>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-securityartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
        dependency>
        <dependency>
            <groupId>org.springframework.securitygroupId>
            <artifactId>spring-security-testartifactId>
            <scope>testscope>
        dependency>
    dependencies>

2.自定义的用户登录认证逻辑

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.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;

import java.util.HashSet;
import java.util.Set;

/**
 * 自定义的用户登录认证逻辑
 */
@Component
public class MyUserDetailsService implements UserDetailsService {
     

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
     
        Set authoritiesSet = new HashSet();
        // 模拟从数据库中获取用户的角色及权限
        GrantedAuthority authority = new SimpleGrantedAuthority("/v3");
        authoritiesSet.add(authority);
        String encode = passwordEncoder.encode("123456");
        // 模拟对比用户密码
        // 登录成功
        return new User( username, encode, authoritiesSet);
    }

}

3.自定义access权限控制

import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.util.Collection;

/**
 * 自定义access权限控制
 */
@Component("rbacauthorityservice")
public class RbacAuthorityService {
     

    public boolean hasPermission(HttpServletRequest request, Authentication authentication) {
     
        Object userInfo = authentication.getPrincipal();
        if (userInfo instanceof UserDetails) {
     
            UserDetails userDetails = (UserDetails) userInfo;
            Collection<? extends GrantedAuthority> authorities = userDetails.getAuthorities();
            String requestURI = request.getRequestURI();
            // 模拟放行无需权限的接口
            if("/v1".equals(request.getRequestURI())){
     
                return true;
            }
            // 验证用户是否有包含该权限
            return authorities.contains(new SimpleGrantedAuthority(requestURI));
        }
        return false;
    }

}

4.自定义的安全校验

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
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.crypto.bcrypt.BCryptPasswordEncoder;

@EnableWebSecurity
@Configuration
public class MyWebSecurityConfigurer extends WebSecurityConfigurerAdapter {
     

    @Autowired
    AjaxAuthenticationEntryPoint authenticationEntryPoint;

    @Autowired
    AjaxAccessDeniedHandler accessDeniedHandler;

    /**
     * 设置默认的加密方式(强hash方式加密)
     */
    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
     
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
     
        // 去掉 CSRF
        http.csrf().disable()
                .authorizeRequests()//定义哪些URL需要被保护、哪些不需要被保护
                .antMatchers(HttpMethod.OPTIONS, "/**").permitAll() // OPTIONS放行
                .antMatchers(HttpMethod.POST, "/login").permitAll()
                .antMatchers(HttpMethod.GET, "/logout").permitAll()
                .anyRequest()
                .access("@rbacauthorityservice.hasPermission(request,authentication)"); // RBAC 动态 url 认证
        http.exceptionHandling().accessDeniedHandler(accessDeniedHandler); // 无权访问 JSON 格式的数据
        http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint);//匿名用户访问无权限资源时的异常处理
    }

}

5.测试类

import com.zm.config.MyUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {
     

    @Autowired
    MyUserDetailsService myUserDetailsService;

    @GetMapping("v1")
    public String v1() {
     
        return "v1 hello world";
    }

    @GetMapping("v2")
    public String v2() {
     
        return "v2 hello world";
    }

    @GetMapping("v3")
    public String v3() {
     
        return "v3 hello world";
    }

    @PostMapping("/login")
    public String login() {
     
        UserDetails userDetails = myUserDetailsService.loadUserByUsername("zhou");
        SecurityContextHolder.getContext()
                .setAuthentication(
                        new UsernamePasswordAuthenticationToken(userDetails,
                                userDetails.getPassword(), userDetails.getAuthorities()));
        return "login ok";
    }

    @PostMapping("/logout")
    public void logout() {
     
        SecurityContextHolder.clearContext();
    }

}

6.测试结果

未登录进行提示
springboot前后端分离整合spring security_第1张图片
登录成功后可以进行访问
springboot前后端分离整合spring security_第2张图片
springboot前后端分离整合spring security_第3张图片

基于spring session集群 分布式中的使用

链接: springboot-整合spring-session-redis在nginx下实现session共享(九).

补充链接: Springboot http session支持分布式;同时支持 cookie 和 header 传递;websocket 连接 共享 http session.

/**
 * 分布式session-redis配置
 */
// session托管到redis
// maxInactiveIntervalInSeconds单位:秒;
// RedisFlushMode有两个参数:ON_SAVE(表示在response commit前刷新缓存),IMMEDIATE(表示只要有更新,就刷新缓存)
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.session.data.redis.config.ConfigureRedisAction;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
import org.springframework.session.web.http.HeaderHttpSessionIdResolver;

@EnableRedisHttpSession(redisNamespace = "session")
@Configuration
public class RedisHttpSessionConfig {
     

    //这里有个小坑,如果服务器用的是云服务器,不加这个会报错
    @Bean
    public static ConfigureRedisAction configureRedisAction() {
     
        return ConfigureRedisAction.NO_OP;
    }

    //可自定义session策略,这里配置的是Header方式(有提供Header,Cookie等方式)
    @Bean
    public HeaderHttpSessionIdResolver httpSessionStrategy() {
     
        // 设置HttpServletRequest中Header里的自定义token名称
        return new HeaderHttpSessionIdResolver("token");
    }

}

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
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.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

@EnableWebSecurity
@Configuration
public class MyWebSecurityConfigurer extends WebSecurityConfigurerAdapter {
     

    @Autowired
    AjaxAuthenticationEntryPoint authenticationEntryPoint;

    @Autowired
    AjaxAccessDeniedHandler accessDeniedHandler;

    /**
     * 设置默认的加密方式(强hash方式加密)
     */
    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
     
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
     
        // 去掉 CSRF
        http.csrf().disable().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER)
                .and().authorizeRequests()//定义哪些URL需要被保护、哪些不需要被保护
                .antMatchers(HttpMethod.OPTIONS, "/**").permitAll() // OPTIONS放行
                .antMatchers(HttpMethod.POST, "/login").permitAll()
                .antMatchers(HttpMethod.GET, "/logout").permitAll()
                .antMatchers(HttpMethod.GET, "/get/**").permitAll()// /get/xxx放行
                .anyRequest()
                .access("@rbacauthorityservice.hasPermission(request,authentication)"); // RBAC 动态 url 认证
        http.exceptionHandling().accessDeniedHandler(accessDeniedHandler); // 无权访问 JSON 格式的数据
        http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint);//匿名用户访问无权限资源时的异常处理
    }

}

基于JWT

分布式项目中的使用

springboot和springsecurity整合OAuth2
Spring Cloud Security OAuth2 实现分布式系统授权
链接: 深入理解Spring Cloud Security OAuth2及JWT.

解决与springboot admin的安全认证

解决方案:配置多个WebSecurityConfigurerAdapter
按Order() 顺序加载

链接: 整合springboot admin 监控 - springboot(十五).

链接: 多个 WebSecurityConfigurerAdapter 的 order 问题.


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpMethod;
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.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

@Order(3)
@EnableWebSecurity
@Configuration
public class MyWebSecurityConfigurer extends WebSecurityConfigurerAdapter {
     

    @Autowired
    AjaxAuthenticationEntryPoint authenticationEntryPoint;

    @Autowired
    AjaxAccessDeniedHandler accessDeniedHandler;

    /**
     * 设置默认的加密方式(强hash方式加密)
     */
    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
     
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
     
        // 去掉 CSRF
        http.csrf().disable().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER)
                .and().authorizeRequests()//定义哪些URL需要被保护、哪些不需要被保护
                .antMatchers(HttpMethod.OPTIONS, "/**").permitAll() // OPTIONS放行
                .antMatchers("/actuator/**").permitAll() // actuator监控接口放行,不指定请求方式

                .antMatchers(HttpMethod.POST, "/login").permitAll()
                .antMatchers(HttpMethod.GET, "/logout").permitAll()
                .antMatchers(HttpMethod.GET, "/get/**").permitAll()
                .anyRequest()
                .access("@rbacauthorityservice.hasPermission(request,authentication)"); // RBAC 动态 url 认证
        http.exceptionHandling().accessDeniedHandler(accessDeniedHandler); // 无权访问 JSON 格式的数据
        http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint);//匿名用户访问无权限资源时的异常处理
    }

}

import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
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.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;

@Order(4)
@Configuration
public class SecuritySecureConfig extends WebSecurityConfigurerAdapter {
     

    /***
     * 指定让它拦截actuator的接口即可,业务相关的接口由业务权限系统去控制
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
     
        http.httpBasic().and().authorizeRequests().antMatchers("/actuator/**").authenticated().anyRequest().permitAll();
        // 关闭CSRF,否则POST请求必须带上token
        http.csrf().disable();
    }

    /**
     * 在内存中配置一个用户,admin/admin分别是用户名和密码,这个用户拥有USER角色。
     * withDefaultPasswordEncoder 被遗弃,原因是不安全,只能在例子中使用
     *
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
     
        PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
        auth.inMemoryAuthentication()
                .withUser("user")
                .password(encoder.encode("user"))
                .roles("USER");
    }
}


参考链接

链接: spring-session(一)揭秘.

链接: Springboot + Spring Security 实现前后端分离登录认证及权限控制.

链接: springboot+springsecurity+mybatis+JWT+Redis 实现前后端离(实战篇).

链接: SpringBoot Security前后端分离登录验证.

链接: SpringBoot+SpringSecurity+jwt实现前后端分离的权限认证(不用security的登陆和注销).

链接: 基于 spring-session 解决分布式 session 共享问题.

你可能感兴趣的:(springboot,spring)