【Spring】SpringSecurity的使用

Spring Security是什么?

Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反转Inversion of Control ,DI:Dependency Injection 依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。


Spring security的使用:

首先引入Spring的核心jar包、SpringMvc的jar以及Security的jar包

http://download.csdn.net/download/evan_qb/10162753


也可以去官网下载

然后我们搭建SpringMvc和Spring Securitiy的环境

配置web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
	<!-- SpringMVC的配置 -->
	<servlet>
	  	<servlet-name>dispatcherServlet</servlet-name>
	  	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  	<init-param>
		<!--用哪种容器类 -->
  		<param-name>contextClass</param-name>
  		<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
  	</init-param>
  	<init-param>
		<!--入口配置 -->
  		<param-name>contextConfigLocation</param-name>
  		<param-value>cn.qblank.config.AppConfig</param-value>
  	</init-param>
  </servlet>
  <servlet-mapping>
  	<servlet-name>dispatcherServlet</servlet-name>
  	<url-pattern>/</url-pattern>
  </servlet-mapping>
  
  <!-- SpringSecuity配置 -->
  <filter>
	<filter-name>springSecurityFilterChain</filter-name>
	<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
  </filter>
  <filter-mapping>
	<filter-name>springSecurityFilterChain</filter-name>
	<url-pattern>/*</url-pattern>
  </filter-mapping>

</web-app>

配置AppConfig类

package cn.qblank.controller.admin;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class UserController {
	@RequestMapping(method = RequestMethod.GET,value = "/admin/users")
	public String showAll(){
		return "admin/users";
	}
	
	@RequestMapping(method = RequestMethod.GET,value = "/login")
	public String show(){
		return "login";
	}
}

配置用户权限管理类实现UserDetailsService接口,输入加密的密码



给每个用户配置对应的权限

package cn.qblank.controller.admin;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

@Service
public class UserDetailServiceImpl implements UserDetailsService{
	@Autowired
	private PasswordEncoder passwordEncoder;
	/**
	 * 用户权限管理
	 */
	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		// 当spring security需要用户详情时调用此方法,传入用户名
		System.out.println("编码:123456 = " + passwordEncoder.encode("123456"));
		System.out.println("编码:admin = " + passwordEncoder.encode("admin"));
		switch(username){
			case "user": 
				return new User("user", "$2a$10$spEZnJMseKZKynWV.X5/AuJjg.hpkeGJNx8ehCdARoNRpa3xLHMmW", "USER");
			case "admin":
				return new User("admin", "$2a$10$OvXZsFpWZlVYwvDa.Q9grOnxeWvAlaLPmtFTZCI7XM5dFfGlb1oVW", "ADMIN");
			case "hr":
				return new User("hr", "$2a$10$spEZnJMseKZKynWV.X5/AuJjg.hpkeGJNx8ehCdARoNRpa3xLHMmW", "ADMIN","HR");
			default:
				throw new UsernameNotFoundException( username+"不存在");
		}
	}

}


配置WebSecurityConfig

package cn.qblank.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
@EnableWebSecurity  // 静态资源、json、校验等支持
//开启web security支持:应用bean容器里的WebSecurityConfigurer
@EnableGlobalMethodSecurity(securedEnabled = true,
//开启注解
prePostEnabled = true)  
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{
	@Autowired
	private UserDetailsService userDetailsService;
	/**
	 * 给用户、静态资源进行权限管理
	 */
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http.authorizeRequests()
		.antMatchers("/login").permitAll()     //给login的访问权
		.antMatchers("/assets/**").permitAll()	//给静态文件的访问权
		.antMatchers("/admin/**").hasRole("ADMIN")	//设置admin路径下的文件只能admin访问
		.antMatchers("/**").authenticated().and()	//给管理员普通赋予普通资源的权限
		.formLogin()	//用于表单
		.loginPage("/login") 	//自定义表单提交页面
		.and()
		//添加记住功能
		.rememberMe()   
		//开启remember Me的支持
		.userDetailsService(userDetailsService)   
		//记住八小时
		.tokenValiditySeconds(8*3600); 
	}
	
	/**
	 * 使用BCryptPasswordEncoder管理密码
	 * @return
	 */
	@Bean
	public PasswordEncoder passwordEncoder(){
		System.out.println("WebSecurityConfig.passwordEncoder()");
		return new BCryptPasswordEncoder();
	}
}

创建User类继承org.springframework.security.core.userdetails.User类

重写构造方法,实现用户权限的添加

package cn.qblank.controller.admin;

import java.util.ArrayList;
import java.util.List;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;

public class User extends org.springframework.security.core.userdetails.User{
	public User(String username,String password,String... roles){
		super(username, password, buildAuthorities(roles));
	}
	
	public static List<GrantedAuthority> buildAuthorities(String[] roles){
		List<GrantedAuthority> authorities = new ArrayList<>();
		System.out.println(roles);
		for (String role : roles) {
			//记住要拼接角色
			authorities.add(new SimpleGrantedAuthority("ROLE_"+role));
		}
		return authorities;
	}
	
	
}

配置自定义的login.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>    

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>登录</title>
<script type="text/javascript" src="/SpringSecurity/assets/js/test.js"></script>
</head>
<body>
	<c:url value="/login" var="loginProcessingUrl"/>
	
   <form action="${loginProcessingUrl}" method="post">
	<!--spring security默认开启CSRF防护,所以所有POST表单都必须包含csrf.token -->
   	<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
    <fieldset>
        <legend>Please Login</legend>
        <c:if test="${param.error != null}">
            <div>
                                 
                <c:if test="${SPRING_SECURITY_LAST_EXCEPTION != null}">
                	用户名或者密码错误
                </c:if>
            </div>
        </c:if>
        <!-- the configured LogoutConfigurer#logoutSuccessUrl is /login?logout and contains the query param logout -->
        <c:if test="${param.logout != null}">
            <div>
                	退出成功,请<a href="${loginProcessingUrl }">登录</a>
            </div>
        </c:if>
        <p>
        <label for="username">用户名</label>
        <input type="text" id="username" name="username"/>
        </p>
        <p>
        <label for="password">密码</label>
        <input type="password" id="password" name="password"/>
        </p>
        <!-- if using RememberMeConfigurer make sure remember-me matches RememberMeConfigurer#rememberMeParameter -->
        <p>
        <label for="remember-me">请记住我?</label>
        <input type="checkbox" id="remember-me" name="remember-me"/>
        </p>
        <div>
            <button type="submit" class="btn">登录</button>
        </div>
    </fieldset>
 </form>
</body>
</html>

每当用户需要访问资源时,都会进行判断看看用户是否登录

例如我们现在访问admin路径下的employees如果用户没登录,就会跳转到登录界面


接下来我们登录admin用户


首先admin路径下的文件需要admin以上用户登录,而getAll()方法必须由hr用户才能访问,并且需要完全登录状态才能访问,本次使用注解实现

public interface EmployeeService {
//	@Secured("ROLE_HR")
	//添加访问权限为全部登录以及是HR用户
	@PreAuthorize("isFullyAuthenticated() && hasRole('HR')")
	List<Employee> findAll();
}

实现类:

package cn.qblank.service.impl;

import java.util.Arrays;
import java.util.List;

import org.springframework.stereotype.Service;

import cn.qblank.entity.Employee;
import cn.qblank.service.EmployeeService;

@Service
public class EmployeeServiceImpl implements EmployeeService	{
	@Override
	public List<Employee> findAll() {
		//使用集合模拟集合
		return Arrays.asList(
				new Employee(1, "张三", "男"),
				new Employee(2,"李四","女"),
				new Employee(3,"王五","男"));
	}

}

所以显示403,没有访问权限,接下来我们使用hr用户登录



上文说到完全登录才能进入该页面,表示通过登录页面进入该页面的称为完全登录,而通过Cookie记住密码,直接进入的叫不完全登陆状态。

接下来我们来测试一下:

清除session,再次输入admin/employee进入登录界面,这次我们勾选记住密码的复选框。


在这里我们需要实现记住密码的功能:

在记住密码中设置name="remember-me"

<label for="remember-me">请记住我?</label>
<input type="checkbox" id="remember-me" name="remember-me"/>

在WebSecurityConfig中开启注解并加上记住密码的配置,并注入remember-me的支持UserDetailService

开启注解

//开启web security支持:应用bean容器里的WebSecurityConfigurer
@EnableGlobalMethodSecurity(securedEnabled = true,
//开启注解
prePostEnabled = true)  
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{

注入UserDetailService

@Autowired
private UserDetailsService userDetailsService;

开启记住密码的配置

/**
 * 给用户、静态资源进行权限管理
 */
@Override
protected void configure(HttpSecurity http) throws Exception {
	http.authorizeRequests()
	.antMatchers("/login").permitAll()     //给login的访问权
	.antMatchers("/assets/**").permitAll()	//给静态文件的访问权
	.antMatchers("/admin/**").hasRole("ADMIN")	//设置admin路径下的文件只能admin访问
	.antMatchers("/**").authenticated().and()	//给管理员普通赋予普通资源的权限
	.formLogin()	//用于表单
	.loginPage("/login") 	//自定义表单提交页面
	.and()
	//添加记住功能
	.rememberMe()   
	//开启remember Me的支持
	.userDetailsService(userDetailsService)   
	//记住八小时
	.tokenValiditySeconds(8*3600); 
}

我们点上记住密码,登录之后我们可以发现Cookie中多了一个remember-me的值


接下来我们重启浏览器


我们可以看到即使有记住密码也需要登陆,这就是不完全登陆


我们还可以使用SpringSecurity进行对不同用户访问的同样界面进行控制

这时我们可以使用Spring-security中的jsp标签库

1.首先导入jar包



2.在jsp中的引入标签库

<%@taglib uri="http://www.springframework.org/security/tags" prefix="ss" %>

3.使用标签进行控制:限定只有hr用户才能看到

<ss:authorize access="hasRole('HR')">
	<a href="${pageContext.request.contextPath }/admin/employees">员工表</a>
</ss:authorize>

接下来我们查看效果:

使用admin用户登陆


使用hr用户登陆


完整项目路径:  https://github.com/qblank/SpringSecurity



没有更多推荐了,返回首页