SpringSecurity学习于实践

SpringSecurity的核心功能

1.认证(你是谁) 2.受权(能干吗) 3.攻击防御(防止伪造身份)html

SpringSecurity的原理

其实Spring Security是一系列的过滤器链 java

在这条顾虑其链上,若是咱们定义的是表单认证,那么UsernamePasswordAuthenticationFilter就会起做用,若是是Basic认证,那么BasicAuthenticationFilter就起做用,这条链上若是不相关的是不会起做用的。最后到达FilterSecurityInterceptor来判断是否能够访问REST API,若是不符合就抛出异常,此时ExceptionTranslationFilter起做用。spring

自定义用户认证

处理用户信息获取逻辑

用户的信息被封装在了org.springframework.security.core.userdetails.UserDetailsService, 若是须要从本身的数据源获取用户数据,那么就须要实现UserDetailsService类,并实现loadUserByUsername(String s)json

@Component//TODO 使之成为用户的bean
public class MyDetailService implements UserDetailsService {
	Logger logger = LoggerFactory.getLogger(MyDetailService.class);
	[@Override](https://my.oschina.net/u/1162528)
	public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
		//TODO 根据用户名查找用户信息
		logger.info("登录用户名:"+s);
		return new User(s,"123456", AuthorityUtils.createAuthorityList("admin"));
}
}

处理用户校验逻辑

能够不使用SpringSecurity的User,能够自定义一个类实现UserDetail,实现它的下面的四个方法。 //帐户是否过时 boolean isAccountNonExpired(); //帐户是否被锁 boolean isAccountNonLocked(); //证书是否过时 boolean isCredentialsNonExpired(); //是否可用 boolean isEnabled();缓存

例子:session

@Component//TODO 使之成为用户的bean
public class MyDetailService implements UserDetailsService {
	Logger logger = LoggerFactory.getLogger(MyDetailService.class);
	[@Override](https://my.oschina.net/u/1162528)
	public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
		//TODO 根据用户名查找用户信息
		logger.info("登录用户名:"+s);
		return new User(s,"123456",true,true,true,false, AuthorityUtils.createAuthorityList("admin"));
	}
}

处理密码加密解密

须要配置一个加密器:app

@Configuration
public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter {
	//生成一个加密器
	@Bean
	public PasswordEncoder getPasswordEncoder(){
		return new BCryptPasswordEncoder();
	}
....}

@Component//TODO 使之成为用户的bean
public class MyDetailService implements UserDetailsService {
**		@Autowired
	PasswordEncoder passwordEncoder;**

	Logger logger = LoggerFactory.getLogger(MyDetailService.class);
	@Override
	public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
		//TODO 根据用户名查找用户信息
		logger.info("登录用户名:"+s);
		String encode = passwordEncoder.encode("123456");
		logger.info("登录用户名:"+encode);
		return new User(s,encode,true,true,true,true, AuthorityUtils.createAuthorityList("admin"));
	}
}

执行结果:ide

2018-11-28 21:23:10.046  INFO 7560 --- [nio-8088-exec-3] com.flexible.service.MyDetailService     : 登录用户名:user
2018-11-28 21:23:10.133  INFO 7560 --- [nio-8088-exec-3] com.flexible.service.MyDetailService     : 登录用户名:$2a$10$S7UuQCqoIEnfzJJGZOnJuuavzZFlohoXA4IpGDkmxmmV9H.01XUIS

自定义认证流程

自定义的登陆界面

http.formLogin().loginPage("/imooc-signIn.html")//这里能够写登陆的界面的url,可是在以前的配置已经默认的拦截因此的url,因此会出现循环的打开登陆的界面,致使死循环,为了解决这种状况就须要使用.antMatchers("/imooc-signIn.html").permitAll()//表示匹配到登陆的时候受权。此时在点击任何该服务的连接就会跳到登陆界面,例如:测试

直接引导登陆界面

http.formLogin()
            .loginPage("/imooc-signIn.html")
            .and()
            .authorizeRequests()//对后面的请求受权
            .antMatchers("/imooc-signIn.html").permitAll()//表示匹配到登陆的时候受权。
            .anyRequest()//任何请求
            .authenticated();//都须要身份认证

实现自定义登陆界面

1.须要请求跳转。flex

2.存储跳转前的请求到httpSessionRequest里面。

3.将请求从缓存取出。

4.判断请求跳转时页面跳转仍是其余的,若是页面就能够时直接跳转(可是要自定义跳转的登陆页面就须要须要实现登陆页面时能够定制化的)。

5.鉴定权限,若是不是就登陆页面的请求跳转就是权限不够提示401。

例子: SecurityProperties.java

@ConfigurationProperties(prefix = "flexible.security")
public class SecurityProperties {
	private BrowserProperties browser = new BrowserProperties();

	public BrowserProperties getBrowser() {
		return browser;
	}

	public void setBrowser(BrowserProperties browser) {
		this.browser = browser;
	}
}

BrowserProperties.java

public class BrowserProperties {
	//配置默认的登陆页
	private String loginPage="/imooc-signIn.html";

	public BrowserProperties() {
	}

	public String getLoginPage() {
		return loginPage;
	}

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

SecurityCoreConfig.java(让配置生)

/**
 * 让咱们配置的security配饰生效
 */
@Configuration
@EnableConfigurationProperties(SecurityProperties.class)
public class SecurityCoreConfig {
}

BrowserSecurityConfig.java(配置放行路径跳转到本身的路径)

http.formLogin()
				.loginPage("/authentication/require")//自定义的认证路径
				.loginProcessingUrl("/authentication/form")//告诉springsecurity使用UsernamePasswordAuthenticationFilter来处理登陆
				.and()
				.authorizeRequests()//对后面的请求受权,将自定义的登陆页面页放权
				.antMatchers("/authentication/require",securityProperties.getBrowser().getLoginPage()).permitAll()//表示匹配到登陆的时候受权。
				.anyRequest()//任何请求
				.authenticated()//都须要身份认证
				.and().csrf().disable();//将spring security为了防止CSRF攻击禁用

BrowserSecurityController.java

@RestController
public class BrowserSecurityController {
	Logger logger = LoggerFactory.getLogger(BrowserSecurityController.class);
	@Autowired
	SecurityProperties securityProperties;//获取到自定义的登陆页
	//拿到引起跳转的请求须要经过HttpSessionRequestCache来拿
	private RequestCache requestCache = new HttpSessionRequestCache();//在跳转以前,springsecurity将请求缓存在这个httpSessionRequestCache里面
	//实现的跳转的一个类
	private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();

	/**
	 * 须要身份认证会跳转到这里
	 * @param request
	 * @param response
	 * @return
	 */
	@RequestMapping("authentication/require")
	@ResponseStatus(code = HttpStatus.UNAUTHORIZED)//不是一个html就返回一个401的状态码
	public SimpleResponse requireAuthentication(HttpServletRequest request, HttpServletResponse response) throws IOException {
		//拿到在跳转以前缓存的请求
		SavedRequest savedRequest = requestCache.getRequest(request, response);
		if (savedRequest != null) {
			String target = savedRequest.getRedirectUrl();
			logger.info("引起跳转的请求是:{}", target);
			if (StringUtils.endsWithIgnoreCase(target, ".html")) {
				//跳转,须要使用到RedirectStrategy
				redirectStrategy.sendRedirect(request,response,securityProperties.getBrowser().getLoginPage());//这里须要实现一个统一,通用的跳转,不单单跳到本身的界面。
			}
		}
		return new SimpleResponse("须要用户认证,引导到登陆页");
	}
}

demo测试例子,在application.properties里面配置一个flexible.security.browser.loginPage="xxx.html"就能够了。

直接访问请他的路径会出现以下的状况:

输入demo-sigIn.html时就会跳到自定义的登陆页

注释掉配置的flexible.security.browser.loginPage="xxx.html"就会跳转默认的登陆页。

自定义登陆成功处理

须要实现AuthenticationSuccessHandler接口

FlexibleAuthenticationHandler.java

@Component(value = "flexibleAuthenticationHandler")
public class FlexibleAuthenticationHandler implements AuthenticationSuccessHandler {
	Logger logger = LoggerFactory.getLogger(FlexibleAuthenticationHandler.class);
	@Autowired
	ObjectMapper objectMapper;
	/**
	 *
	 * @param httpServletRequest
	 * @param httpServletResponse
	 * @param authentication 封装了认证请求的信息(ip,session,用户信息)
	 * @throws IOException
	 * @throws ServletException
	 */
	@Override
	public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
	logger.info("....login success...");
	httpServletResponse.setContentType("application/json;charset=UTF-8");
	//将authentication对象一json格式返回去
	httpServletResponse.getWriter().write(objectMapper.writeValueAsString(authentication));
	}
}

须要告诉SpringSecurity成功后的处理

.successHandler(flexibleAuthenticationHandler)//告诉SpringSecurity成功以后使用的处理器

自定义失败处理

须要实现AuthenticationFailureHandler接口

FlexibleAuthenticationFailureHandler.java

@Component(value = "flexibleAuthenticationFailureHandler")
public class FlexibleAuthenticationFailureHandler implements AuthenticationFailureHandler {
	Logger logger = LoggerFactory.getLogger(FlexibleAuthenticationFailureHandler.class);

	@Autowired
	ObjectMapper objectMapper;

	@Override
	public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException authentication) throws IOException, ServletException {

		logger.info("....login failure...");
		httpServletResponse.setContentType("application/json;charset=UTF-8");
		httpServletResponse.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
		//将authentication对象一json格式返回去
		httpServletResponse.getWriter().write(objectMapper.writeValueAsString(authentication));
	}
}

告诉SpringSecurity失败后的处理器:

.failureHandler(flexibleAuthenticationFailureHandler)//告诉SpringSecurity失败后的处理器

记住个人功能的实现

实现的原理剖析

相关文章
相关标签/搜索