spring cloud 要放一放了,
先看看spring security怎么用吧。
这个主要是根着慕课网的一个实战视频http://coding.imooc.com/class/134.html
来学习。
认证(你是谁)
授权(你能干什么)
攻击防护(防止伪造身份)
spring security 添加后,启动springboot
会看到
Using default security password: 1e4970e3-a687-449b-aa71-bb2fe158bdae
用户名 默认为user
可以自己编写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表单认证
BasicAuthenticationFilter 处理httpbasic登录
UsernamePasswordAuthenticationFilter 处理表单登录
FilterSecurityInterceptor 最后一层 当前请求能否访问api
ExceptionTranslationFilter 捕获异常 根据异常引导对应页面
封装在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"));//把字符串转成集合
}
}
密码是否匹配,用户是否失效等等。。
详细看UserDetails
isAccountNonExpired()账户是否过期
isAccountNonLocked()账户是否被冻结
isCredentialsNonExpired()密码是否过期
isEnabled()账户是否有效
org.springframework.security.crypto.password.PasswordEncoder
encode 加密 插入数据时调用。
matches匹配 登录时比对
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 {
}
然后就可以读取配置了
实现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
自定义跳转页面
新建类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
自定义跳转页面
在filter中创建了
usernamepasswordAuthenticationToken对象,里面封装了用户名和密码,还有权限,当前信息是否进行了身份认证。
然后调用authenticationManager,作用管理authenticationProvider
authenticationProvider循环判断支不支持,选择支持的继续执行
执行authenticate方法-》执行retrieveUser方法
调用UserDetailsService 返回Authentication对象
再执行successHandler
如果在执行过程中有异常都会被捕获
unSuccessfulAuthentication
SecurityContext 包装了Authentication
SecurityContextHolder 是ThreadLocal封装
请求 检查session是否有securityContext,有就放入线程
响应 检查线程是否有securityContext,有就放入session
方法上直接加 Authentication,或者去securityContext获取
这里我跳过了很多 ,oauth和springsocial,jwt等等..
角色众多,权限复杂
新建接口 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;
}
}