展开
拓展 关闭
订阅号推广码
GitHub
视频
公告栏 关闭

security学习案例总结

前言

博客一

# pom.xml
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
    </dependencies>

# yml
server:
  port: 8080

# 配置类配置用户名和密码
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication().withUser("test").password(passwordEncoder().encode("123456"))
                .authorities("admin");
    }
}
  • 测试
# 浏览器访问 http://localhost:8080/hello

# 输入用户名和密码test:123456

博客二

  • 参考
  • 新建项目demo02,在demo01的基础上开发
# 在WebSecurityConfig配置类中添加如下:
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 1、表单操作 表单请求成功处理器、失败处理器;与loginPage冲突,配置后,loginPage不生效
        FormLoginConfigurer<HttpSecurity> formLogin = http.formLogin();
        formLogin.loginPage(null);// 自定义登录页的路由(静态页面,或者mvc视图式的页面地址)
        formLogin.loginProcessingUrl(null);// 登录接口的路由地址:必须为post请求的接口地址
        formLogin.isCustomLoginPage();// 是否为自定义登录页,true为自定义的登录页;
        formLogin.passwordParameter(null);// 登录的表单的 密码的字段名称
        formLogin.usernameParameter(null);// 登录的表单的 用户名的字段名称

        formLogin.successForwardUrl(null);// 登录成功跳转的url
        formLogin.successHandler(null);// 登录成功的拦截器
        formLogin.defaultSuccessUrl(null);// 默认的成功的url

        formLogin.failureForwardUrl(null);// 失败跳转的url
        formLogin.failureHandler(null);// 失败的拦截器
        formLogin.failureUrl(null);// 失败的url

        formLogin.permitAll();// 放开不拦截,默认为true
        formLogin.permitAll(false);

        formLogin.disable();// 关闭表单的登录(其实只是拦截器链路上的一个拦截器)

        formLogin.init(http);// 初始化方法
        formLogin.authenticationDetailsSource(null);
        formLogin.addObjectPostProcessor(null);
        formLogin.withObjectPostProcessor(null);
        HttpSecurity and = formLogin.and();
        formLogin.setBuilder(and);
        formLogin.configure(http);

        http.addFilter(null);
        http.addFilterAfter(null, null);
        http.addFilterAt(null, null);

        AnonymousConfigurer<HttpSecurity> anonymous = http.anonymous();
        http.anonymous(null);

        http.cors();
        http.cors(null);
        http.csrf();
        http.csrf(null);

        http.headers();
        http.headers(null);
        http.httpBasic();
        http.httpBasic(null);

        http.logout().addLogoutHandler(null);
        http.logout(null);

        http.oauth2Client();
        http.oauth2Client(null);
        http.oauth2Login();
        http.oauth2Login(null);
        http.oauth2ResourceServer();
        http.oauth2ResourceServer(null);

        http.rememberMe();
        http.rememberMe(null);

        http.sessionManagement();
        http.sessionManagement(null);

        http.exceptionHandling();
        http.exceptionHandling(null);
        http.userDetailsService(null);
        http.authenticationProvider(null);

        http.antMatcher(null);

        http.apply(null);

        http.authorizeRequests().antMatchers("/**/*.js", "/**/*.css").permitAll().anyRequest().hasRole("admin");
        http.authorizeRequests();
        http.authorizeRequests(null);

        http.build();
    }
  • 表单登录选项: 表单请求成功处理器、失败处理器;与loginPage冲突,配置后,loginPage不生效
        formLogin.loginPage(null);//自定义登录页的路由(静态页面,或者mvc视图式的页面地址)
        formLogin.loginProcessingUrl(null);//登录接口的路由地址:必须为post请求的接口地址
        formLogin.isCustomLoginPage();// 是否为自定义登录页,true为自定义的登录页;
        formLogin.passwordParameter(null);// 登录的表单的 密码的字段名称
        formLogin.usernameParameter(null);//登录的表单的 用户名的字段名称

        formLogin.successForwardUrl(null);// 登录成功跳转的url
        formLogin.successHandler(null);// 登录成功的拦截器
        formLogin.defaultSuccessUrl(null);// 默认的成功的url

        formLogin.failureForwardUrl(null);// 失败跳转的url
        formLogin.failureHandler(null);// 失败的拦截器
        formLogin.failureUrl(null);// 失败的url

        formLogin.permitAll();// 放开不拦截,默认为true
        formLogin.permitAll(false);
		formLogin.disable();// 关闭表单的登录(其实只是拦截器链路上的一个拦截器)
	    //其他配置项
		formLogin.init(http);// 初始化方法
        formLogin.authenticationDetailsSource(null);
        formLogin.addObjectPostProcessor(null);
        formLogin.withObjectPostProcessor(null);
        HttpSecurity and = formLogin.and();
        formLogin.setBuilder(and);
        formLogin.configure(http);

  • 过滤器
        http.addFilter(null);// 增加一个拦截器
        http.addFilterAfter(null, null);//增加一个拦截器 在xxx拦截器之后
        http.addFilterAt(null, null);//增加一个拦截器 在xxx拦截器之前
  • 其他
# 匿名用户
        http.anonymous();
# 跨域
        http.cors();
        http.cors(null);
# csrf
        http.csrf();
        http.csrf(null);
# httpbasic,前后端分离时关闭
        http.httpBasic();
        http.httpBasic(null);
# 登出
        http.logout();
        http.logout(null);
        http.logout().addLogoutHandler(null);//配置登出处理器
        http.logout().disable();// 关闭登出操作;等jwt超时
# 记住我
        http.rememberMe();
        http.rememberMe(null);
# session会话管理
       http.sessionManagement();
       http.sessionManagement(null);
       http.sessionManagement().disable();// 关闭session 管理器
# 认证处理器
       http.userDetailsService(null);//帐号密码认证
       http.authenticationProvider(null);//认证处理
# 鉴权管理
       http.authorizeRequests();
       http.authorizeRequests(null);
# 异常处理
       http.exceptionHandling();
       http.exceptionHandling(null);
  • oauth2
       http.oauth2Client();
       http.oauth2Client(null);
       http.oauth2Login();
       http.oauth2Login(null);
       http.oauth2ResourceServer();
       http.oauth2ResourceServer(null);
  • 启动报错
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2022-04-05 19:46:28.199 ERROR 22748 --- [           main] o.s.boot.SpringApplication               : Application run failed

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'springSecurityFilterChain' defined in class path resource [org/springframework/security/config/annotation/web/configuration/WebSecurityConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [javax.servlet.Filter]: Factory method 'springSecurityFilterChain' threw exception; nested exception is java.lang.IllegalArgumentException: Pattern cannot be null or empty
	at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:655) ~[spring-beans-5.2.8.RELEASE.jar:5.2.8.RELEASE]

博客三

  • 参考
  • 启动项目报错:Error creating bean with name 'xmlModelPlugin': Lookup method resolution failed
  • 解决方案:注释swagger2相关的代码
  • pom.xml
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.78</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>
  • 返回数据工具类
@Data
public class Result {
    private Integer code;
    private String message;
    private Result(Integer code, String messgae) {
        this.code = code;
        this.message = messgae;
    }
    public static Result success(String message) {
        return new Result(200, message);
    }
    public static Result result(Integer code, String messgae) {
        return new Result(code, messgae);
    }
}
  • security配置类
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    // 放行的接口
    private static final String[] EXCLUDE_URLS = { "/**/*.js", "/**/*.css", "/**/*.jpg", "/**/*.png", "/**/*.gif",
            "/v2/**", "/errors", "/error", "/favicon.ico", "/swagger-ui.html/**", "/swagger-ui/**", "/webjars/**",
            "/swagger-resources/**", "/auth/login" };
    @Autowired
    private AuthenticationSuccessHandler successHandler;
    @Autowired
    private AuthenticationFailureHandler failureHandler;
    @Autowired
    AccessDeniedHandler deniedHandler;
    @Autowired
    AuthenticationEntryPoint entryPoint;
    // 加密方式
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    // 配置用户名、密码
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication().withUser("test").password(passwordEncoder().encode("123456"))
                .authorities("admin");
    }
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 全局异常配置
        http.exceptionHandling().accessDeniedHandler(deniedHandler).authenticationEntryPoint(entryPoint);
        // 放行的路由接口
        http.authorizeRequests().antMatchers(EXCLUDE_URLS).permitAll();
        // 1、表单操作
        FormLoginConfigurer<HttpSecurity> formLogin = http.formLogin();
        // 表单请求成功处理器、失败处理器;与loginPage冲突,配置后,loginPage不生效
        formLogin.successHandler(successHandler).failureHandler(failureHandler);
        // 表单提交的post请求地址,用户参数名称
        formLogin.loginProcessingUrl("/auth/login");
        // 关闭csrf防护
        http.csrf().disable();
    }
}
  • 拒绝访问处理器
@Component
public class DeniedHandler implements AccessDeniedHandler {
    @SuppressWarnings("deprecation")
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response,
            AccessDeniedException accessDeniedException) throws IOException, ServletException {
        String error = "请求Url:" + request.getRequestURI() + " 鉴权失败:" + accessDeniedException.getMessage();
        response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
        PrintWriter writer = response.getWriter();
        writer.print(JSON.toJSONString(Result.result(HttpStatus.UNAUTHORIZED.value(), error)));
        writer.flush();
        writer.close();
    }
}
  • 未认证处理器
@Component
public class EntryPoint implements AuthenticationEntryPoint {
    @SuppressWarnings("deprecation")
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response,
            AuthenticationException authException) throws IOException, ServletException {
        String error = "请求Url:" + request.getRequestURI() + " 认证失败:" + authException.getMessage();
        response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
        PrintWriter writer = response.getWriter();
        writer.print(JSON.toJSONString(Result.result(HttpStatus.UNAUTHORIZED.value(), error)));
        writer.flush();
        writer.close();
    }
}
  • 认证成功处理器
@Component
@Slf4j
public class SuccessHandler implements AuthenticationSuccessHandler {
    @SuppressWarnings("deprecation")
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
            Authentication authentication) throws IOException, ServletException {
        response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
        log.info("登录成功:{}", authentication.getPrincipal());
        PrintWriter writer = response.getWriter();
        writer.print(JSON.toJSONString(Result.success(authentication.getPrincipal().toString())));
        writer.flush();
        writer.close();
    }
}
  • 认证失败处理器
@Component
public class FailureHandler implements AuthenticationFailureHandler {
    private Logger log = LoggerFactory.getLogger(getClass());
    @SuppressWarnings("deprecation")
    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
            AuthenticationException exception) throws IOException, ServletException {
        log.info("登录失败,{}", exception.getMessage());
        response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
        PrintWriter writer = response.getWriter();
        writer.print(JSON.toJSONString(Result.result(400, exception.getMessage())));
        writer.flush();
        writer.close();
    }
}
  • 登录接口编写
@RestController
public class LoginController {
    @PostMapping("/auth/login")
    public void login(@RequestParam(name = "username", required = true) String username,
            @RequestParam(name = "password", required = true) String password) {
    }
}
  • 测试

博客五

  • 参考

  • 新建demo04,在demo03的基础上开发

  • 使用用户名+密码或手机号+密码或者邮箱+密码登录,但并没有构建Provider

  • 认证流程

# 首先发送请求会进入AuthenticationFilter,由AuthenticationFilter生成一个UserNamepasswordAuthenticationToken
# 之后UserNamepasswordAuthenticationToken进入AuthenticationManager
# AuthenticationManager主要进行认证
# AuthenticationManager中有多个AuthenticationProviders
# 每个AuthenticationProviders是不同的认证机制
# 之后通过UserDetailsService查询数据库获取用户信息
# 最后按原来的流程将认证成功后的用户信息放到Authentication中

1. 向手机发送手机验证码,使用第三方短信平台 SDK 发送,如: 阿里云短信服务(阿里大于)
2. 登录表单输入短信验证码
3. 使用自定义过滤器 MobileValidateFilter 
4. 当验证码校验通过后,进入自定义手机认证过滤器 MobileAuthenticationFilter 校验手机号是否存在
5. 自定义 MobileAuthenticationToken 提供给  MobileAuthenticationFilter
6. 自定义 MobileAuthenticationProvider 提供给 ProviderManager 处理
7. 创建针对手机号查询用户信息的  MobileUserDetailsService ,交给  MobileAuthenticationProvider
8. 自定义 MobileAuthenticationConfig 配置类将上面组件连接起来,添加到容器中
9. 将 MobileAuthenticationConfig 添加到 SpringSecurityConfig 安全配置的过滤器链上
  • UsernamePasswordAuthenticationProvider
@Slf4j
public class UsernamePasswordAuthenticationProvider implements AuthenticationProvider {

    @Getter
    @Setter
    private UserDetailsService userDetailsService;
    @Autowired
    private PasswordEncoder passwordEncoder;

    public UsernamePasswordAuthenticationProvider(UserDetailsService userDetailsService) {
        super();
        this.userDetailsService = userDetailsService;
    }

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        // 获取表单用户名
        String username = (String) authentication.getPrincipal();
        // 获取表单用户填写的密码
        String password = (String) authentication.getCredentials();
        UserDetails userDetails;
        try {
            userDetails = userDetailsService.loadUserByUsername(username);
        } catch (Exception e) {
            // 异常不是鉴权异常的时候,异常无法向上抛出,异常处理的controller无法返回默认异常,需要把异常处理成鉴权异常
            throw new AuthenticationServiceException(e.getMessage());
        }
        if (!passwordEncoder.matches(password, userDetails.getPassword())) {
            log.info("当前登录人:{},当前登录密码:{}", username, password);
            throw new BadCredentialsException("用户密码不正确");
        }
        return new UsernamePasswordAuthenticationToken(username, null, userDetails.getAuthorities());
    }

    @Override
    public boolean supports(Class<?> authentication) {
        // 帐号密码登录使用的校验器
        return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
    }
}
  • SecurityConfig
@Configuration
public class SecurityConfig {

    static final List<UserEntity> USER_LIST = new ArrayList<>();

    static {
        for (int i = 1; i < 6; i++) {
            UserEntity userEntity = new UserEntity();
            userEntity.setId(i);
            userEntity.setName("测试人员" + i);
            userEntity.setUsername("ceshi_" + i);
            // 密码使用 PasswordEncoder 类对123456 加密之后的结果
            userEntity.setPassword("$2a$10$D1q09WtH./yTfFTh35n0k.o6yZIXwxIW1/ex6/EjYTF7EiNxXyF7m");
            userEntity.setEmail("100" + i + "@qq.com");
            userEntity.setPhone("186xxxx351" + i);
            USER_LIST.add(userEntity);
        }
    }

    /************************帐号密码登录**********************/
    @Bean
    public UserDetailsService usernamePasswordUserDetails() {
        return new UserDetailsService() {
            @Override
            public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
                UserEntity user = null;
                for (UserEntity userEntity : USER_LIST) {
                    if (username.equals(userEntity.getUsername()) || username.equals(userEntity.getPhone())
                            || username.equals(userEntity.getEmail())) {
                        user = userEntity;
                    }
                }
                if (user != null) {
                    return new SystemUserDetails(user.getUsername(), user.getPassword(), user, null);
                }
                throw new UsernameNotFoundException("用户未注册,请先注册");
            }
        };
    }

    @Bean
    public AuthenticationProvider usernamePasswordAuthenticationProvider() {
        return new UsernamePasswordAuthenticationProvider(usernamePasswordUserDetails());
    }
    
}
  • 启动项目后,curl测试通过
curl -X POST "http://localhost:8080/auth/login?username=ceshi_1&password=123456" -H "accept: */*"
curl -X POST "http://localhost:8080/auth/login?username=1001@qq.com&password=123456" -H "accept: */*"
curl -X POST "http://localhost:8080/auth/login?username=186xxxx3511&password=123456" -H "accept: */*"

博客六

posted @ 2022-04-05 19:18  DogLeftover  阅读(59)  评论(0编辑  收藏  举报