Skip to content

spring security

yiqun233 edited this page Jun 5, 2018 · 9 revisions

spring security

简介

程序安全性的两个主要方面是“认证”和”授权“,这两个概念都很好理解。

认证

Spring Security 支持多种认证模式。这些验证绝大多数都是要么由第三方提供,或由相关的标准组织,如互联网工程任务组开发。另外Spring Security 提供自己的一组认证功能。

  • BASIC
  • OAnth2.0(微信QQ)
  • LDAP
  • CAS(政府企业)

授权

授权方向主要有三种权限:

  1. url权限:(authorizing web requests)授权web请求,允许url的跳转

  2. 方法权限:(authorizing whether methods can be invoked)授权方法是否可以被调用

  3. 数据权限:(authorizing access to individual domain object instances)授权可以访问数据域

配置

可以在application.yml文件中配置信息。

依赖

使用Maven可能会用到以下依赖:

<dependency>
	<groupId>org.springframework.security</groupId>
	<artifactId>spring-security-web</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.security</groupId>
	<artifactId>spring-security-config</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-test</artifactId>
    <scope>test</scope>
</dependency>

<dependency>
    <groupId>org.thymeleaf.extras</groupId>
    <artifactId>thymeleaf-extras-springsecurity4</artifactId>
</dependency>

java配置

使用spring security前的一些java配置

package com.example.conf;

public class SecurityWebApplicationInitializer
	extends AbstractSecurityWebApplicationInitializer {

	public SecurityWebApplicationInitializer() {
		super(WebSecurityConfig.class);
	}
}

SecurityWebApplicationInitializer作用:

  • 自动为应用程序的每个URL注册 springSecurityFilterChain过滤器.

  • 添加一个ContextLoadListener 用来载入 WebSecurityConfig(下面的).

package com.example.conf;

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

	@Autowired
	public void configureGlobal(AuthenticationManagerBuilder auth)
			throws Exception {
		auth.inMemoryAuthentication().withUser("yiqun").password("123456")
				.roles("USER");
	}
}

WebSecurityConfig配置功能:

  • 在你的应用程序中对每个URL进行验证

  • 为你生成一个登陆表单(默认的,可以自己写新的)

  • 允许使用用户名 ”yiqun“和密码 ”123456“使用验证表单进行验证。(并设置权限)

  • 允许用户登出

  • CSRF attack CSPF攻击防范

  • Session Fixation Session保护

  • 安全 Header 集成

  • 和以下 Servlet API 方法集成

URL权限

在WebSecurityConfig可以添加以下配置:

	public void configure(WebSecurity web) throws Exception {
		web.ignoring().antMatchers("/other/**"); 
		//可以将CSS,JS等静态资源放在目录位置,将全部忽略跳过
	}

WebSecurity和HttpSecurity 的区别是HttpSecurity 只能是url请求。

protected void configure(HttpSecurity http) throws Exception {
		http.authorizeRequests()
				.antMatchers("/signup", "/about").permitAll() // #1
				.antMatchers("/admin/**").hasRole("ADMIN") // #2
				.anyRequest().authenticated() // #3 
				.and()
			.formLogin()
				.loginPage("/login") //#4
				.permitAll() //#5
				.and()
			.logout() //#6        
			.logoutRequestMatcher(newAntPathRequestMatcher("/logout")) //#7
				.logoutUrl("/logout") //#8
				.logoutSuccessUrl("/login") //#9                                             
				.logoutSuccessHandler(logoutSuccessHandler) //#10 
				.invalidateHttpSession(true) //#11
				.addLogoutHandler(logoutHandler) //#12 
				.deleteCookies(cookieNamesToClear)  //#13                                     
				.and()
			.httpBasic(); //#14
		http.csrf()
				.disable(); //#15
				.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()); //#16
		
	}
  1. 允许所有权限访问指定URL
  2. 设置只有指定 权限的人才能访问指定URL
  3. 除了以上指定URL , 其他访问都需要验证
  4. 自己指定登录URL,需要自己在controller写一个
  5. 允许所有用户访问所有基于表单登录的URL
  6. 提供注销支持 ,AntPathRequestMatcher会自动应用
  7. 提供注销支持并且指定跳转页面
  8. 设置触发注销操作的URL (默认是/logout,可以不写), 如果CSRF内启用(默认是启用的)的话这个请求的方式被限定为POST。
  9. 注销成功后跳转的URL(默认是/login?logout),指定url之后会跳到指定的注销页面,不会注销后二次登陆
  10. 自己设置logoutSuccessHandler,如果指定了这个选项那么logoutSuccessUrl()的设置会被忽略。
  11. 指定是否在注销时让HttpSession无效。 默认设置为 true 。
  12. 添加一个LogoutHandler.SecurityContextLogoutHandler默认添加最后一个LogoutHandler
  13. 允许指定在注销成功时将移除的cookie。
  14. 允许用户使用HTTP基于验证进行认证
  15. 禁用CSRF
  16. 开启CSRF,在页面有CSRF隐藏域的配置,必须使用#7

方法权限

获取用户信息三种方式:SecurityContextHolder方法(后端),sec页面获取(添加依赖和页面开头),js方法从从session获取。

开发一般是自己重写从数据库获取的方式

方法权限service

package com.example.service;
@Service

public class MethodSecurityService {
	@Secured("ROLE_ADMIN")
	public String roleAdmin() {
		String role = "我是一个admin";
		return role;
	}

	@PreAuthorize("hasRole('ROLE_ADMIN' )and #num>5")
	public void test1(int num) {
		System.out.println("拥有权限并且数字大于5");
	}

	@PostAuthorize("returnObjict == 'abc'")
	public String test2() {
		System.out.println("执行方法并且返回一个字符对象");
		return "abcd";
	}

	@PreAuthorize("hasAnyRole({'ROLE_ADMIN','ROLE_USER'})")
	@PostFilter("hasRole('ROLE_ADMIN') || " + "filterObject.username == principal.username")
	public List<User> test3() {
		User u1 = new User("u1", "男", 19, "123");
		User u2 = new User("u2", "女", 19, "123");
		User u3 = new User("u3", "男", 19, "123");
		List<User> list = new ArrayList<>();
		list.add(u1);
		list.add(u2);
		list.add(u3);
		return list;
	}

	@PreAuthorize("hasAnyRole({'ROLE_ADMIN','ROLE_USER'})")
	@PostFilter("hasRole('ROLE_ADMIN') || " + "filterObject.username == principal.username")
	public void test4(List<User> users) {
		for (User user : users) {
			System.out.println(user.getLoginName());
		}
	}

//以下内容为主要功能
	public List<User> list = new ArrayList<>();

	/**
	 * 显示所有用户信息
	 */
	public List<User> showList() {
		return list;
	}

	/**
	 * 登陆后根据权限显示信息
	 */
	@PreAuthorize("hasAnyRole({'ROLE_ADMIN','ROLE_USER'})")
	@PostFilter("hasRole('ROLE_ADMIN') || " + "filterObject.loginName == principal.username")
	public List<User> userForm() {
		User u1 = new User("admin", "123", 23, "男");
		User u2 = new User("user", "123", 23, "男");
		User u3 = new User("易群", "123", 23, "男");
		list.add(u1);
		list.add(u2);
		list.add(u3);
		for (int i = 0; i < list.size() - 1; i++) {
			for (int j = list.size() - 1; j > i; j--) {
				if (list.get(j).getLoginName().equals(list.get(i).getLoginName())) {
					list.remove(j);
				}
			}
		}
		return list;
	}

	/**
	 * 根据用户名删除信息
	 */
	public List<User> delete(String del) {
		for (User user : list) {
			if (user.getLoginName().equals(del)) {
				list.remove(user);
				break;
			}
		}
		return list;
	}
}

相关代码

登录页login:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
	xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
<!-- Thymeleaf为我们提供的Spring Security的标签支持 -->
<head>
<meta charset="UTF-8" />
<title>登录页</title>
</head>
<body>
<form name='f' action='login' method='POST'>
<table>
	<tr>
		<td>用户名:</td>
		<td><input type='text' name='username' value='admin'/></td>
	</tr>
	<tr>
		<td>密码:</td><td><input type='password' name='password' value='123'/></td>
	</tr>
	<tr>
		<td colspan='2'><input name="submit" type="submit" value="Login"/></td>
	</tr>
	<input type="hidden"                        
		th:name="${_csrf.parameterName}"
		th:value="${_csrf.token}"
		th:if="${_csrf}"/>
</table>
</form>
</body>
</html>

用户页hello:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
	xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
<!-- Thymeleaf为我们提供的Spring Security的标签支持 -->
<head>
<meta content="text/html;charset=UTF-8" />
<title sec:authentication="name"></title>
<!-- 获得当前用户的用户名 -->
</head>
<body>
<fieldset >
<legend>登录用户:<span sec:authentication="name"></span>
		登录角色:<span sec:authentication="principal.authorities"></span>
</legend>
<br></br>
<table border="1" align="center" id="tbl" >
	<tr>
		<th width="50px"><label th:text="用户名"></label></th>
		<th width="50px"><label th:text="密码"></label></th>
		<th width="50px"><label th:text="年龄"></label></th>
		<th width="50px"><label th:text="性别"></label></th>
		<th width="50px"><label th:text="删除"></label></th>
	</tr>
	<tr th:each="user: ${list}">
		<td align="center" th:text="${user.loginName}"> </td>
		<td align="center" th:text="${user.password}"> </td>
		<td align="center" th:text="${user.age}"> </td>
		<td align="center" th:text="${user.sex}"> </td>
		<td align="center"><a th:href="@{'/delete?loginName='+${user.loginName}}">删除</a></td>
	</tr>
</table>
	<ul sec:authorize="isAnonymous()" >
		<li><a th:href="@{/login}">Login </a></li>
		<li><a th:href="@{/register}">Sign Up</a></li>
	</ul>
	<ul sec:authorize="isAuthenticated()">
		<li><a th:href="@{logout}">Log out</a></li>
	</ul>
	<ul sec:authorize="isAuthenticated()">
		<li><a th:href="@{'/add'}">添加</a></li>
	</ul>
</fieldset>
</body>
<style type="text/css">
fieldset {
	width: 500px;
	height: 250px;
	margin: 100px auto;
	padding: 20px;
}
</style>
</html>

controller:

package com.example.controller;
@Controller
public class DemoController {
	
@Autowired
MethodSecurityService methodSecurityService;


	/**
	 * 跳转到登录页面
	 */
	@GetMapping("/login")
	public String login() {
		return "login";
	}
	
	/**
	 * 登录成功跳转页面
	 */
	@GetMapping("/")
	public String hello(Model model) {
		@SuppressWarnings("unused")
		UserDetails userDetails = (UserDetails) SecurityContextHolder.getContext()
			    .getAuthentication()
			    .getPrincipal();
		model.addAttribute("list",methodSecurityService.userForm());
		return "hello";
	}
	
	/**
	 * 显示所有信息
	 */
	@GetMapping("/show")
	public String show(Model model) {
		@SuppressWarnings("unused")
		UserDetails userDetails = (UserDetails) SecurityContextHolder.getContext()
			    .getAuthentication()
			    .getPrincipal();
		model.addAttribute("list",methodSecurityService.showList());
		return "hello";
	}
	
	/**
	 * 根据用户名删除信息
	 */
	@GetMapping("/delete")
	public String deletet(User user) {
		methodSecurityService.delete(user.getLoginName());
		return "redirect:show";
	}
	
}

Clone this wiki locally