spring security详解

作者: adm 分类: java 发布时间: 2021-08-04

1.springsecurity

springsecurity底层实现为一条过滤器链,就是用户请求进来,判断有没有请求的权限,抛出异常,重定向跳转。

2.登录页

springsecurity自带一个登录页。

从登陆入手,登录页替换成我们自己的,对输入的账号密码进行验证

/**

* 表单登陆security

* 安全 = 认证 + 授权

*/

@Configuration

public class SecurityConfig extends WebSecurityConfigurerAdapter {

@Override

protected void configure(HttpSecurity http) throws Exception {

//以下五步是表单登录进行身份认证最简单的登陆环境

http.formLogin() //表单登陆 1

.and() //2

.authorizeRequests() //下面的都是授权的配置 3

.anyRequest() //任何请求 4

.authenticated(); //访问任何资源都需要身份认证 5

}

}

  

如果只实现一个WebSecurityConfigurerAdapter然后重写一下configure方法,效果会默认使用springsecurity的登录页 ,以及项目启动时后台会打印出一个默认的密码,然后使用任意账号就可以进行登录访问指定的资源

3.自定义登录页 与 UserDetailsService 用户名密码校验

如果想要使用自己的登录页 并且用户名密码是自己数据库中的,进一步完善spring security认证体系,首先需要做以下配置。

@Override

protected void configure(HttpSecurity http) throws Exception {

//以下五步是表单登录进行身份认证最简单的登陆环境

http.formLogin() //表单登陆 1

.loginPage("/login.html") //指定登陆页面

.and() //2

.authorizeRequests() //下面的都是授权的配置 3

.antMatchers("/login.html").permitAll()//访问此地址就不需要进行身份认证了,防止重定向死循环

.anyRequest() //任何请求 4

.authenticated(); //访问任何资源都需要身份认证 5

}

  

然后实现UserDetailsService接口进行用户姓名密码校验 (由于springboot2.x中security是5.x版本的,所以这里的密码是默认做了BCrypt加密的,就需要bean一个BCrypt)

@Component

public class MyUserDetailService implements UserDetailsService {

//注入mapper

//...

@Autowired

private PasswordEncoder passwordEncoder;

private Logger LOG = LoggerFactory.getLogger(MyUserDetailService.class);

@Override

public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {

LOG.error("登陆用户输入的用户名:{}",s);

//根据用户名查找用户信息

//密码进行bcrypt加密

String pwd = "wangkai";

//String cryptPwd = BCrypt.hashpw(pwd, BCrypt.gensalt());

String cryptPwd = passwordEncoder.encode(pwd);

LOG.error("加密后的密码为: {}",cryptPwd);

return new User("s",cryptPwd, AuthorityUtils.commaSeparatedStringToAuthorityList("admin")); //账号 密码 权限

}

}

/**

* 表单登陆security

* 安全 = 认证 + 授权

*/

@Configuration

public class SecurityConfig extends WebSecurityConfigurerAdapter {

/**

* 介绍

* springboot2.x引入的security版本是5.x的,这个版本需要提供一个PasswordEncoder实例,不然就会报错

* @return

*/

@Bean

public PasswordEncoder passwordEncoder() {

return new BCryptPasswordEncoder();

}

@Override

protected void configure(HttpSecurity http) throws Exception {

//以下五步是表单登录进行身份认证最简单的登陆环境

http.formLogin() //表单登陆 1

.loginPage("/login.html") //指定登陆页面

.and() //2

.authorizeRequests() //下面的都是授权的配置 3

.antMatchers("/login.html").permitAll()//访问此地址就不需要进行身份认证了,防止重定向死循环

.anyRequest() //任何请求 4

.authenticated(); //访问任何资源都需要身份认证 5

}

}

4.登陆页面提交页面 /authentication/form

添加登陆页面提交页面,关闭跨站请求伪造攻击,登陆访问资源

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登陆</title>
</head>
<body>
<h2>标准登陆页面</h2>
<h3>表单登陆</h3>
<form action = "/authentication/form" method ="post">
<table>
<tr>
<td>用户名:</td>
<td><input type="text" name="username"></td>
</tr>
<tr>
<td>密码:</td>
<td><input type="password" name="password"></td>
</tr>
<tr>
<td colspan="2"><button type="submit">登陆</button></td>
</tr>
</table>
</form>
</body>
</html>
@Override

protected void configure(HttpSecurity http) throws Exception {

//以下五步是表单登录进行身份认证最简单的登陆环境

http.formLogin() //表单登陆 1

.loginPage("/login.html") //指定登陆页面

.loginProcessingUrl("/authentication/form")//登陆页面提交的页面 开始使用UsernamePasswordAuthenticationFilter过滤器处理请求

.and() //2

.authorizeRequests() //下面的都是授权的配置 3

.antMatchers("/login.html").permitAll()//访问此地址就不需要进行身份认证了,防止重定向死循环

.anyRequest() //任何请求 4

.authenticated() //访问任何资源都需要身份认证 5

.and()

.csrf().disable();//关闭跨站请求伪造攻击拦截

}

  

5.动态配置登录页

  

.做一个我们自己默认的登录页,如果不想用默认的也可以动态配置。使用到的注解@ConfigurationProperties。

.增加接口/authentication/require

.引导用户进入登录页登陆

@Override

protected void configure(HttpSecurity http) throws Exception {

//以下五步是表单登录进行身份认证最简单的登陆环境

http.formLogin() //表单登陆 1

//.loginPage("/login.html") //指定登陆页面

.loginPage("/authentication/require")

.loginProcessingUrl("/authentication/form")//登陆页面提交的页面 开始使用UsernamePasswordAuthenticationFilter过滤器处理请求

.and() //2

.authorizeRequests() //下面的都是授权的配置 3

.antMatchers("/login.html",

"/authentication/require",

securityProperties.getBrowser().getLoginPage()).permitAll()//访问此地址就不需要进行身份认证了,防止重定向死循环

.anyRequest() //任何请求 4

.authenticated() //访问任何资源都需要身份认证 5

.and()

.csrf().disable();//关闭跨站请求伪造攻击拦截

}
@RestController

public class BrowserSecurityController {

private Logger LOG = LoggerFactory.getLogger(BrowserSecurityController.class);

//将当前请求缓存到session里

private RequestCache requestCache = new HttpSessionRequestCache();

private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();

@Resource

private SecurityProperties securityProperties;

/**

* 当需要身份认证时跳转到这里

* @param request

* @param response

* @return

*/

@RequestMapping(value = "/authentication/require",method = RequestMethod.GET)

@ResponseStatus(code = HttpStatus.UNAUTHORIZED) //未授权状态码

public SimpleResponse requireAuthentication(HttpServletRequest request, HttpServletResponse response) throws IOException {

//拿到引发跳转的请求

SavedRequest savedRequest = requestCache.getRequest(request,response);

if(savedRequest != null){

String targetUrl = savedRequest.getRedirectUrl();

String fileUrl=new URL(targetUrl).getFile();

LOG.info("引发跳转的请求是:{}",targetUrl);

if(StringUtils.endsWithIgnoreCase(targetUrl,".html") || fileUrl.equals("/")){

//调转到登录页 》》这里登录页做成可配置的

redirectStrategy.sendRedirect(request,response,securityProperties.getBrowser().getLoginPage());

}

}

return new SimpleResponse("访问资源需要登陆,请访问登陆页面");

}

}

从配置文件中读取当访问资源需要身份认证调转的页面地址

server.port=8888

#自定义springsecurity 登录页面

security.browser.loginPage = /mylogin.html
package com.example.security.properties;

import com.example.security.pojo.SecurityBrowserPojo;

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

/**

* 实现动态配置用户专属登陆页面

*/

@ConfigurationProperties(prefix = "security")

public class SecurityProperties {

private SecurityBrowserPojo browser = new SecurityBrowserPojo();

public SecurityBrowserPojo getBrowser() {

return browser;

}

public void setBrowser(SecurityBrowserPojo browser) {

this.browser = browser;

}

}
public class SecurityBrowserPojo {

//设置默认地址

private String loginPage = "/login.html";

public String getLoginPage() {

return loginPage;

}

public void setLoginPage(String loginPage) {

this.loginPage = loginPage;

}

}
package com.example.security.config.securityconfig;

import com.example.security.properties.SecurityProperties;

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

import org.springframework.context.annotation.Configuration;

@Configuration

@EnableConfigurationProperties({SecurityProperties.class}) //设置注解读取生效 (试了下不用配置这里@ConfigurationProperties也可以生效)

public class SecurityPropertiesConfig {

}

5.登陆成功/登陆失败处理

某些时候用户登陆成功,登陆失败的时候可能还需要做一些操作,比如成功登陆增加一积分之类的操作,这里需要做两个handler处理器

/**

* 设置通过请求拦截。登陆成功后处理

*/

@Component("wawAuthenticationSuccessHandler")

public class WawAuthenticationSuccessHandler implements AuthenticationSuccessHandler {

private Logger LOG = LoggerFactory.getLogger(WawAuthenticationSuccessHandler.class);

@Resource

private ObjectMapper objectMapper;

/**

* @param authentication 封装认证信息>>用户信息 请求ip之类的

* @throws IOException

* @throws ServletException

*/

@Override

public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {

LOG.info("登陆成功");

response.setContentType("application/json;charset=UTF-8");

response.getWriter().write(objectMapper.writeValueAsString(authentication));

}

}
/**

* 设置通过请求拦截。登陆失败后处理

*/

@Component("wawAuthenticationFailHandler")

public class WawAuthenticationFailHandler implements AuthenticationFailureHandler{

private Logger LOG = LoggerFactory.getLogger(WawAuthenticationFailHandler.class);

@Resource

private ObjectMapper objectMapper;

@Override

public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {

LOG.info("登陆失败");

response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());

response.setContentType("application/json;charset=UTF-8");

response.getWriter().write(objectMapper.writeValueAsString(e));

}

}

成功与失败的处理器 配置到配置信息中

@Override

protected void configure(HttpSecurity http) throws Exception {

//以下五步是表单登录进行身份认证最简单的登陆环境

http.formLogin() //表单登陆 1

//.loginPage("/login.html") //指定登陆页面

.loginPage("/authentication/require")

.loginProcessingUrl("/authentication/form")//登陆页面提交的页面 开始使用UsernamePasswordAuthenticationFilter过滤器处理请求

.successHandler(wawAuthenticationSuccessHandler)

.failureHandler(wawAuthenticationFailHandler)

.and() //2

.authorizeRequests() //下面的都是授权的配置 3

.antMatchers("/authentication/require",

"/login.html",

securityProperties.getBrowser().getLoginPage()).permitAll()//访问此地址就不需要进行身份认证了,防止重定向死循环

.anyRequest() //任何请求 4

.authenticated() //访问任何资源都需要身份认证 5

.and()

.csrf().disable();//关闭跨站请求伪造攻击拦截

}

登陆失败就会返回500 登陆异常信息

 

如果觉得我的文章对您有用,请随意赞赏。您的支持将鼓励我继续创作!