spring security

spring cloud 要放一放了,
先看看spring security怎么用吧。
这个主要是根着慕课网的一个实战视频http://coding.imooc.com/class/134.html
来学习。

一 简介

1 核心功能

认证(你是谁)
授权(你能干什么)
攻击防护(防止伪造身份)

二 基本原理

#

spring security 添加后,启动springboot
会看到

Using default security password: 1e4970e3-a687-449b-aa71-bb2fe158bdae

用户名 默认为user

默认为basic认证
spring security_第1张图片

可以自己编写security的配置类,BrowserSecurityConfig

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
public class BrowserSecurityConfig  extends WebSecurityConfigurerAdapter{


    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.formLogin() 表单认证
        //http.httpBasic() //basic认证
        .and()
        .authorizeRequests()//认证请求
        .anyRequest()//任何请求
        .authenticated();//都需要身份认证
    }


}

现配置为form表单认证

2原理

spring security_第2张图片
BasicAuthenticationFilter 处理httpbasic登录
UsernamePasswordAuthenticationFilter 处理表单登录
FilterSecurityInterceptor 最后一层 当前请求能否访问api
ExceptionTranslationFilter 捕获异常 根据异常引导对应页面

三 基于表单验证

1.处理用户信息获取逻辑

封装在UserdetailsService 接口 ,Userdetails loadUserByUsername(String username)
如果通过了校验,seurity会把Userdetails放到session中

import org.springframework.security.core.authority.AuthorityUtils;
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.stereotype.Component;

@Component
public class MyUserDetailsService implements UserDetailsService{

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //根据用户名查找用户信息
        return new User(username,"123456",AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));//把字符串转成集合
    }

}

2.处理用户校验逻辑

密码是否匹配,用户是否失效等等。。
详细看UserDetails

isAccountNonExpired()账户是否过期
isAccountNonLocked()账户是否被冻结
isCredentialsNonExpired()密码是否过期
isEnabled()账户是否有效

这里写图片描述
7个参数的构造方法

3.处理密码加密解密

org.springframework.security.crypto.password.PasswordEncoder

encode 加密 插入数据时调用。
matches匹配 登录时比对

三个性化认证流程

1. 自定义登录页面

1)在Resource下新建resource目录,目录下新建自定义登录页面
在sercurity的配置类中配置页面

http.formLogin().loginPage("xxx")

添加匹配项,(如果不配置会死循环)

.antMatchers("/zzz").permitAll()
http.formLogin().loginPage("zzz")//表单认证
        //http.httpBasic() //basic认证
        .loginProcessingUrl("xxx")
        .and()
        .authorizeRequests()//认证请求
        .antMatchers("/zzz").permitAll()
        .anyRequest()//任何请求
        .authenticated();//都需要身份认证

loginProcessingUrl("xxx")是为了配置登录具体的url,默认为login

2)自定义配置
appication.properties 添加

security.browser.loginPage = /demo-signIn.html

新建类 BrowserProperties和 SucurityProperties

public class BrowserProperties {
    private String loginPage;

    public String getLoginPage() {
        return loginPage;
    }

    public void setLoginPage(String loginPage) {
        this.loginPage = loginPage;
    }


}
import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties(prefix="security")//读取security开头的配置文件
public class SecurityProperties {

    private BrowserProperties browserProperties = new BrowserProperties();

    public BrowserProperties getBrowserProperties() {
        return browserProperties;
    }

    public void setBrowserProperties(BrowserProperties browserProperties) {
        this.browserProperties = browserProperties;
    }


}

还需要新建配置类,使SecurityProperties配置生效

import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;

import com.sucurity.core.properties.SecurityProperties;

/**
 * 使SecurityProperties配置生效
 */

@Configuration
@EnableConfigurationProperties(SecurityProperties.class)
public class SecurityCoreConfig {

}

然后就可以读取配置了

2.登录成功

实现AuthenticationSuccessHandler

@Component("myAuthenticationSuccessHandler")
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler{

    @Autowired
    private ObjectMapper objectMapper;

    /**
     * authentication 封装的认证请求的信息
     */
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
            Authentication authentication) throws IOException, ServletException {
        // TODO Auto-generated method stub
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().write(objectMapper.writeValueAsString(authentication));
    }

}

修改BrowserSecurityConfig
注入

@Autowired
    private MyAuthenticationSuccessHandler myAuthenticationSuccessHandler ;

在loginUrl后添加

.successHandler(myAuthenticationSuccessHandler)

下面是返回的json

{
    "authorities": [{
        "authority": "admin"
    }],
    "details": {
        "remoteAddress": "0:0:0:0:0:0:0:1",
        "sessionId": "4E6523BAC8C8DC7D403ADD9EA5BE127A"
    },
    "authenticated": true,
    "principal": {
        "password": null,
        "username": "jojo",
        "authorities": [{
            "authority": "admin"
        }],
        "accountNonExpired": true,
        "accountNonLocked": true,
        "credentialsNonExpired": true,
        "enabled": true
    },
    "credentials": null,
    "name": "jojo"
}

也可以继承SavedRequestAwareAuthenticationSuccessHandler
自定义跳转页面

3.登录失败

新建类MyAuthenticationFailureHandler

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;

import com.fasterxml.jackson.databind.ObjectMapper;

@Component("myAuthenticationFailureHandler")
public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler{

    @Autowired
    private ObjectMapper objectMapper;

    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
            AuthenticationException exception) throws IOException, ServletException {
        response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().write(objectMapper.writeValueAsString(exception));

    }

}

添加配置

.failureHandler(myAuthenticationFailureHandler)

失败打印信息

{"cause":null,"stackTrace":[{"methodName":"additionalAuthenticationChecks","fileName":"DaoAuthenticationProvider.java","lineNumber":98,"className":"org.springframework.security.authentication.dao.DaoAuthenticationProvider","nativeMethod":false},{"methodName":"authenticate","fileName":"AbstractUserDetailsAuthenticationProvider.java","lineNumber":166,"className":"org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider","nativeMethod":false},{"methodName":"authenticate","fileName":"ProviderManager.java","lineNumber":174,"className":"org.springframework.security.authentication.ProviderManager","nativeMethod":false},{"methodName":"authenticate","fileName":"ProviderManager.java","lineNumber":199,"className":"org.springframework.security.authentication.ProviderManager","nativeMethod":false},{"methodName":"attemptAuthentication","fileName":"UsernamePasswordAuthenticationFilter.java","lineNumber":94,"className":"org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter","nativeMethod":false},{"methodName":"doFilter","fileName":"AbstractAuthenticationProcessingFilter.java","lineNumber":212,"className":"org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter","nativeMethod":false},{"methodName":"doFilter","fileName":"FilterChainProxy.java","lineNumber":331,"className":"org.springframework.security.web.FilterChainProxy$VirtualFilterChain","nativeMethod":false},{"methodName":"doFilter","fileName":"LogoutFilter.java","lineNumber":116,"className":"org.springframework.security.web.authentication.logout.LogoutFilter","nativeMethod":false},{"methodName":"doFilter","fileName":"FilterChainProxy.java","lineNumber":331,"className":"org.springframework.security.web.FilterChainProxy$VirtualFilterChain","nativeMethod":false},{"methodName":"doFilterInternal","fileName":"HeaderWriterFilter.java","lineNumber":66,"className":"org.springframework.security.web.header.HeaderWriterFilter","nativeMethod":false},{"methodName":"doFilter","fileName":"OncePerRequestFilter.java","lineNumber":107,"className":"org.springframework.web.filter.OncePerRequestFilter","nativeMethod":false},{"methodName":"doFilter","fileName":"FilterChainProxy.java","lineNumber":331,"className":"org.springframework.security.web.FilterChainProxy$VirtualFilterChain","nativeMethod":false},{"methodName":"doFilter","fileName":"SecurityContextPersistenceFilter.java","lineNumber":105,"className":"org.springframework.security.web.context.SecurityContextPersistenceFilter","nativeMethod":false},{"methodName":"doFilter","fileName":"FilterChainProxy.java","lineNumber":331,"className":"org.springframework.security.web.FilterChainProxy$VirtualFilterChain","nativeMethod":false},{"methodName":"doFilterInternal","fileName":"WebAsyncManagerIntegrationFilter.java","lineNumber":56,"className":"org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter","nativeMethod":false},{"methodName":"doFilter","fileName":"OncePerRequestFilter.java","lineNumber":107,"className":"org.springframework.web.filter.OncePerRequestFilter","nativeMethod":false},{"methodName":"doFilter","fileName":"FilterChainProxy.java","lineNumber":331,"className":"org.springframework.security.web.FilterChainProxy$VirtualFilterChain","nativeMethod":false},{"methodName":"doFilterInternal","fileName":"FilterChainProxy.java","lineNumber":214,"className":"org.springframework.security.web.FilterChainProxy","nativeMethod":false},{"methodName":"doFilter","fileName":"FilterChainProxy.java","lineNumber":177,"className":"org.springframework.security.web.FilterChainProxy","nativeMethod":false},{"methodName":"invokeDelegate","fileName":"DelegatingFilterProxy.java","lineNumber":347,"className":"org.springframework.web.filter.DelegatingFilterProxy","nativeMethod":false},{"methodName":"doFilter","fileName":"DelegatingFilterProxy.java","lineNumber":263,"className":"org.springframework.web.filter.DelegatingFilterProxy","nativeMethod":false},{"methodName":"internalDoFilter","fileName":"ApplicationFilterChain.java","lineNumber":193,"className":"org.apache.catalina.core.ApplicationFilterChain","nativeMethod":false},{"methodName":"doFilter","fileName":"ApplicationFilterChain.java","lineNumber":166,"className":"org.apache.catalina.core.ApplicationFilterChain","nativeMethod":false},{"methodName":"doFilterInternal","fileName":"RequestContextFilter.java","lineNumber":99,"className":"org.springframework.web.filter.RequestContextFilter","nativeMethod":false},{"methodName":"doFilter","fileName":"OncePerRequestFilter.java","lineNumber":107,"className":"org.springframework.web.filter.OncePerRequestFilter","nativeMethod":false},{"methodName":"internalDoFilter","fileName":"ApplicationFilterChain.java","lineNumber":193,"className":"org.apache.catalina.core.ApplicationFilterChain","nativeMethod":false},{"methodName":"doFilter","fileName":"ApplicationFilterChain.java","lineNumber":166,"className":"org.apache.catalina.core.ApplicationFilterChain","nativeMethod":false},{"methodName":"doFilterInternal","fileName":"HttpPutFormContentFilter.java","lineNumber":109,"className":"org.springframework.web.filter.HttpPutFormContentFilter","nativeMethod":false},{"methodName":"doFilter","fileName":"OncePerRequestFilter.java","lineNumber":107,"className":"org.springframework.web.filter.OncePerRequestFilter","nativeMethod":false},{"methodName":"internalDoFilter","fileName":"ApplicationFilterChain.java","lineNumber":193,"className":"org.apache.catalina.core.ApplicationFilterChain","nativeMethod":false},{"methodName":"doFilter","fileName":"ApplicationFilterChain.java","lineNumber":166,"className":"org.apache.catalina.core.ApplicationFilterChain","nativeMethod":false},{"methodName":"doFilterInternal","fileName":"HiddenHttpMethodFilter.java","lineNumber":81,"className":"org.springframework.web.filter.HiddenHttpMethodFilter","nativeMethod":false},{"methodName":"doFilter","fileName":"OncePerRequestFilter.java","lineNumber":107,"className":"org.springframework.web.filter.OncePerRequestFilter","nativeMethod":false},{"methodName":"internalDoFilter","fileName":"ApplicationFilterChain.java","lineNumber":193,"className":"org.apache.catalina.core.ApplicationFilterChain","nativeMethod":false},{"methodName":"doFilter","fileName":"ApplicationFilterChain.java","lineNumber":166,"className":"org.apache.catalina.core.ApplicationFilterChain","nativeMethod":false},{"methodName":"doFilterInternal","fileName":"CharacterEncodingFilter.java","lineNumber":197,"className":"org.springframework.web.filter.CharacterEncodingFilter","nativeMethod":false},{"methodName":"doFilter","fileName":"OncePerRequestFilter.java","lineNumber":107,"className":"org.springframework.web.filter.OncePerRequestFilter","nativeMethod":false},{"methodName":"internalDoFilter","fileName":"ApplicationFilterChain.java","lineNumber":193,"className":"org.apache.catalina.core.ApplicationFilterChain","nativeMethod":false},{"methodName":"doFilter","fileName":"ApplicationFilterChain.java","lineNumber":166,"className":"org.apache.catalina.core.ApplicationFilterChain","nativeMethod":false},{"methodName":"doFilterInternal","fileName":"MetricsFilter.java","lineNumber":106,"className":"org.springframework.boot.actuate.autoconfigure.MetricsFilter","nativeMethod":false},{"methodName":"doFilter","fileName":"OncePerRequestFilter.java","lineNumber":107,"className":"org.springframework.web.filter.OncePerRequestFilter","nativeMethod":false},{"methodName":"internalDoFilter","fileName":"ApplicationFilterChain.java","lineNumber":193,"className":"org.apache.catalina.core.ApplicationFilterChain","nativeMethod":false},{"methodName":"doFilter","fileName":"ApplicationFilterChain.java","lineNumber":166,"className":"org.apache.catalina.core.ApplicationFilterChain","nativeMethod":false},{"methodName":"invoke","fileName":"StandardWrapperValve.java","lineNumber":198,"className":"org.apache.catalina.core.StandardWrapperValve","nativeMethod":false},{"methodName":"invoke","fileName":"StandardContextValve.java","lineNumber":96,"className":"org.apache.catalina.core.StandardContextValve","nativeMethod":false},{"methodName":"invoke","fileName":"AuthenticatorBase.java","lineNumber":496,"className":"org.apache.catalina.authenticator.AuthenticatorBase","nativeMethod":false},{"methodName":"invoke","fileName":"StandardHostValve.java","lineNumber":140,"className":"org.apache.catalina.core.StandardHostValve","nativeMethod":false},{"methodName":"invoke","fileName":"ErrorReportValve.java","lineNumber":81,"className":"org.apache.catalina.valves.ErrorReportValve","nativeMethod":false},{"methodName":"invoke","fileName":"StandardEngineValve.java","lineNumber":87,"className":"org.apache.catalina.core.StandardEngineValve","nativeMethod":false},{"methodName":"service","fileName":"CoyoteAdapter.java","lineNumber":342,"className":"org.apache.catalina.connector.CoyoteAdapter","nativeMethod":false},{"methodName":"service","fileName":"Http11Processor.java","lineNumber":803,"className":"org.apache.coyote.http11.Http11Processor","nativeMethod":false},{"methodName":"process","fileName":"AbstractProcessorLight.java","lineNumber":66,"className":"org.apache.coyote.AbstractProcessorLight","nativeMethod":false},{"methodName":"process","fileName":"AbstractProtocol.java","lineNumber":790,"className":"org.apache.coyote.AbstractProtocol$ConnectionHandler","nativeMethod":false},{"methodName":"doRun","fileName":"NioEndpoint.java","lineNumber":1459,"className":"org.apache.tomcat.util.net.NioEndpoint$SocketProcessor","nativeMethod":false},{"methodName":"run","fileName":"SocketProcessorBase.java","lineNumber":49,"className":"org.apache.tomcat.util.net.SocketProcessorBase","nativeMethod":false},{"methodName":"runWorker","fileName":"ThreadPoolExecutor.java","lineNumber":1142,"className":"java.util.concurrent.ThreadPoolExecutor","nativeMethod":false},{"methodName":"run","fileName":"ThreadPoolExecutor.java","lineNumber":617,"className":"java.util.concurrent.ThreadPoolExecutor$Worker","nativeMethod":false},{"methodName":"run","fileName":"TaskThread.java","lineNumber":61,"className":"org.apache.tomcat.util.threads.TaskThread$WrappingRunnable","nativeMethod":false},{"methodName":"run","fileName":"Thread.java","lineNumber":745,"className":"java.lang.Thread","nativeMethod":false}],"localizedMessage":"坏的凭证","message":"坏的凭证","suppressed":[]}

也可以继承ExceptionMappingAuthenticationFailureHandler
自定义跳转页面

四.认证流程

1.认证的处理流程说明

spring security_第3张图片

在filter中创建了
usernamepasswordAuthenticationToken对象,里面封装了用户名和密码,还有权限,当前信息是否进行了身份认证。

然后调用authenticationManager,作用管理authenticationProvider

authenticationProvider循环判断支不支持,选择支持的继续执行

执行authenticate方法-》执行retrieveUser方法
调用UserDetailsService 返回Authentication对象
再执行successHandler

如果在执行过程中有异常都会被捕获
unSuccessfulAuthentication

2.认证结果如何在多个请求间共享

spring security_第4张图片

SecurityContext 包装了Authentication
SecurityContextHolder 是ThreadLocal封装
spring security_第5张图片

请求 检查session是否有securityContext,有就放入线程
响应 检查线程是否有securityContext,有就放入session

3.获取认证用户信息

方法上直接加 Authentication,或者去securityContext获取

这里我跳过了很多 ,oauth和springsocial,jwt等等..

五.基于数据库RBAC配置

角色众多,权限复杂

spring security_第6张图片

新建接口 RbacService

import javax.servlet.http.HttpServletRequest;

import org.springframework.security.core.Authentication;

public interface RbacService {

    boolean hasPermission(HttpServletRequest request, Authentication authentication);

}

实现类 RbacServiceImpl

import java.util.Set;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang.StringUtils;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;

import com.earthchen.security.rbac.domain.Admin;
import com.earthchen.security.rbac.service.RbacService;

@Component("rbacService")
public class RbacServiceImpl implements RbacService {

    private AntPathMatcher antPathMatcher = new AntPathMatcher();

    @Override
    public boolean hasPermission(HttpServletRequest request, Authentication authentication) {
        Object principal = authentication.getPrincipal();

        boolean hasPermission = false;

        if (principal instanceof Admin) {
            //如果用户名是admin,就永远返回true
            if (StringUtils.equals(((Admin) principal).getUsername(), "admin")) {
                hasPermission = true;
            } else {
                // 读取用户所拥有权限的所有URL
                Set urls = ((Admin) principal).getUrls();
                for (String url : urls) {
                    if (antPathMatcher.match(url, request.getRequestURI())) {
                        hasPermission = true;
                        break;
                    }
                }
            }
        }

        return hasPermission;
    }

}

在DemoAuthorizeConfigProvider 配置

import com.earthchen.security.core.authorize.AuthorizeConfigProvider;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.stereotype.Component;


@Component
@Order(Integer.MAX_VALUE)
public class DemoAuthorizeConfigProvider implements AuthorizeConfigProvider {

    /**
     * demo项目授权配置
     * @param config
     * @return
     */
    @Override
    public boolean config(ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry config) {
        //config.antMatchers("/demo.html").hasRole("ADMIN");
        config.anyRequest().access("@rbacService.hasPermission(request,authentication)");
        return true;

    }
}

你可能感兴趣的:(学习,spring,spring,security)