Spring Security 简介
Spring Security 是一个能够为基于 Spring 的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在 Spring 应用上下文中配置的 Bean,充分利用了
Spring IoC,DI(控制反转 Inversion of Control ,DI:Dependency Injection 依赖注入)和 AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。
1.引入依赖
在spring依赖引入的基础上,引入spring Security依赖,版本可以使用spring的版本
<!--
spring Security/身份验证
-->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
</dependency>
2.配置web.xml文件
1)添加加载Security配置的监听器,
2)注册安全认证的过滤器链
这些过滤器实际是在spring容器中管理,这里只是代理注册给web容器
<!-- spring Security 安全认证 监听器,读取配置文件,创建security容器 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/spring-security.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--
spring Security 安全认证 监听器,过滤器链在web中注册
这些过滤器实际是在spring容器中管理,这里只是代理注册给web容器
-->
<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>
3.添加配置文件 spring-security.xml
使用静态用户(死数据)
<?xml version="1.0" encoding="UTF-8"?>
<bean:beans xmlns="http://www.springframework.org/schema/security"
xmlns:bean="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-4.1.xsd">
<!--
1、设置放行资源,如登录注册页面,静态资源css、js等等
security="none" 设置此资源不被拦截.
-->
<http pattern="/login.html" security="none"></http>
<http pattern="/loginerror.html" security="none"></http>
<http pattern="/css/**" security="none"></http>
<http pattern="/img/**" security="none"></http>
<http pattern="/js/**" security="none"></http>
<http pattern="/plugins/**" security="none"></http>
<http>
<!-- 2、拦截所有(除放行资源外) -->
<intercept-url pattern="/**" access="hasRole('ROLE_USER')" />
<!--
3、登录表单设置
1)login-page:指定登录页面;
2)login-processing-url:指定登录请求路径;
3)default-target-url:指定了成功进行身份验证和授权后默认呈现给用户的页面;
4)always-use-default-target:指定了是否在身份验证通过后总是跳转到;
default-target-url 属性指定的 URL。
5)authentication-failure-url:指定了身份验证失败时跳转到的页面;
-->
<form-login login-page="/login.html"
login-processing-url="/login"
always-use-default-target="true"
default-target-url="/admin/index.html"
authentication-failure-url="/loginerror.html"
/>
<!-- 4、注销设置
1)logout-url:指定注销的url;
2)logout-success-url:注销成功后登录返回的页面。
-->
<logout logout-url="/logout" logout-success-url="/login.html"/>
<!--
5、跨站请求设置(我们这里关闭)
1)csrf disabled="true" 关闭 csrf ,如果不加会出现错误
2)CSRF(Cross-site request forgery):跨站请求伪造,
也被称为“One Click Attack”或者 SessionRiding,
通常缩写为 CSRF 或者 XSRF,是一种对网站的恶意利用。
-->
<csrf disabled="true" />
<!-- 6、iframe 框架结构展示 -->
<headers>
<frame-options policy="SAMEORIGIN" />
</headers>
</http>
<!--
认证管理器
1)我们这里设置一个默认用户
-->
<authentication-manager>
<authentication-provider>
<user-service>
<user authorities="ROLE_USER" name="admin" password="123456" />
</user-service>
</authentication-provider>
</authentication-manager>
</bean:beans>
错误一、未关闭跨域请求,csrf disabled=”true” 关闭 csrf ,如果不加会出现错误
错误二,未放行登录页面,如果你没有设置登录页 security=”none” ,将会出现以下错误
简单的登录页面
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>登陆</title>
</head>
<body>
<form action='/login' method='POST'>
<table>
<tr>
<td>用户名:</td>
<td><input type='text' name='username' value=''></td>
</tr>
<tr>
<td>密码:</td>
<td><input type='password' name='password' /></td>
</tr>
<tr>
<td colspan='2'><input name="submit" type="submit"
value=" 登陆 " /></td>
</tr>
</table>
</form>
</body>
</html>
4、数据库动态校验用户
用户认证类
package com.it.manager.service;
import java.util.ArrayList;
import java.util.List;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
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 com.it.manager.service.SellerService;
import com.it.pojo.TbSeller;
public class userDetailServiceImpl implements UserDetailsService {
private SellerService sellerService;
public SellerService getSellerService() {
return sellerService;
}
public void setSellerService(SellerService sellerService) {
this.sellerService = sellerService;
}
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
List<GrantedAuthority> grantedAuths = new ArrayList<GrantedAuthority>();
//这里设置一个默认权限,也可以通过数据库查询出用户访问权限进行设置
grantedAuths.add(new SimpleGrantedAuthority("ROLE_USER"));
// 调用sellerService查询用户
TbSeller seller = sellerService.findOne(username);
if (seller != null && seller.getPassword() != null) {
//查询返回一个Security框架规定的用户对象,并将用户帐号,密码及权限信息以构造方式传入
return new User(username, seller.getPassword(), grantedAuths);
} else {
return null;
}
}
}
在SpringSecurity.xml中设置:
1)注册自定义认证类,并注入到认证管理器中
2)设置加密方式
BCrypt 加密算法
用户表的密码通常使用 MD5 等不可逆算法加密后存储,为防止彩虹表破解更会先使用一个特定的字符串(如域名)加密,然后再使用一个随机的 salt(盐值)加密。 特定字符
串是程序代码中固定的,salt 是每个密码单独随机,一般给用户表加一个字段单独存储,比较麻烦。 BCrypt 算法将 salt 随机并混入最终加密后的密码,验证时也无需单独提供之前的
salt,从而无需单独处理 salt 问题。
<?xml version="1.0" encoding="UTF-8"?>
<bean:beans xmlns="http://www.springframework.org/schema/security"
xmlns:bean="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-4.1.xsd
http://code.alibabatech.com/schema/dubbo
http://code.alibabatech.com/schema/dubbo/dubbo.xsd
">
<!--
1、设置放行资源,如登录注册页面,静态资源css、js等等
security="none" 设置此资源不被拦截.
-->
<http pattern="/login.html" security="none"></http>
<http pattern="/loginerror.html" security="none"></http>
<http pattern="/css/**" security="none"></http>
<http pattern="/img/**" security="none"></http>
<http pattern="/js/**" security="none"></http>
<http pattern="/plugins/**" security="none"></http>
<http pattern="/seller/insert" security="none"></http>
<http pattern="/shoplogin.html" security="none"></http>
<http pattern="/register.html" security="none"></http>
<http>
<!-- 2、拦截所有(除放行资源外) -->
<intercept-url pattern="/**" access="hasRole('ROLE_USER')" />
<!--
3、登录表单设置
1)login-page:指定登录页面;
2)login-processing-url:指定登录请求路径;
3)default-target-url:指定了成功进行身份验证和授权后默认呈现给用户的页面;
4)always-use-default-target:指定了是否在身份验证通过后总是跳转到;
default-target-url 属性指定的 URL。
5)authentication-failure-url:指定了身份验证失败时跳转到的页面;
-->
<form-login
login-page="/shoplogin.html"
login-processing-url="/login"
always-use-default-target="true"
default-target-url="/admin/index.html"
authentication-failure-url="/loginerror.html" />
<!-- 4、注销设置
1)logout-url:指定注销的url;
2)logout-success-url:注销成功后登录返回的页面。
-->
<logout logout-url="/logout" logout-success-url="/login.html" />
<!--
5、跨站请求设置(我们这里关闭)
1)csrf disabled="true" 关闭 csrf ,如果不加会出现错误
2)CSRF(Cross-site request forgery):跨站请求伪造,
也被称为“One Click Attack”或者 SessionRiding,
通常缩写为 CSRF 或者 XSRF,是一种对网站的恶意利用。
-->
<csrf disabled="true" />
<!-- 6、iframe 框架结构展示 -->
<headers>
<frame-options policy="SAMEORIGIN" />
</headers>
</http>
<!--
二、 认证管理器
1、引用userDetailService作为用户登录验证的服务类
2、设置密码加密方式
-->
<authentication-manager alias="authenticationManager">
<!-- 注入自定义认证类对象 -->
<authentication-provider user-service-ref='userDetailService'>
<!-- 注入加密算法类 -->
<password-encoder ref="bcryptEncoder"></password-encoder>
</authentication-provider>
</authentication-manager>
<!-- 2、注册加密算法类,使用BCryptPasswordEncoder加密方式 -->
<bean:bean id="bcryptEncoder"
class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" />
<!-- 三、引用dubbo 服务 ,
用于分布式远程调用服务提供者的注入,
这里需要引入dubbo相关的依赖,非分布式项目这里无需引入-->
<!-- 1、定义表现层服务名称: 服务消费者名称 -->
<dubbo:application name="shop-web-2" />
<!-- 2、指定服务消费地址 -->
<dubbo:registry address="zookeeper://192.168.11.11:2181" />
<!-- 3、xml方式引入服务 -->
<dubbo:reference id="sellerService"
interface="com.it.manager.service.SellerService">
</dubbo:reference>
<!-- 4、使用注解方法引入服务 -->
<dubbo:annotation package="com.it.shop.controller" />
<!-- 四、注册认证类 -->
<bean:bean id="userDetailService" class="com.it.manager.service.userDetailServiceImpl">
<bean:property name="sellerService" ref="sellerService">
</bean:property>
</bean:bean>
</bean:beans>
用户注册时的加密
@RequestMapping("/register")
public Result add(UserEntity userEntity){
//密码加密
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
String password = passwordEncoder.encode(userEntity.getPassword());
userEntity.setPassword(password);
try {
UserService.register(userEntity);
return new Result(true, "增加成功");
} catch (Exception e) {
e.printStackTrace();
return new Result(false, "增加失败");
}
}
五、权限设置(注解方式)
在业务层方法上通过注解@PreAuthorize,配置调用方法需要的权限:
例如:@PreAuthorize(“hasAuthority(‘PRODUCT_LIST’)”)
/**
*
* 查询所有商品
* @PreAuthorize("hasAuthority('PRODUCT_LIST')")
* 配置调用该业务层方法需要的权限为:PRODUCT_LIST
* 该注解为,在调用业务方法之前验证权限
* */
@PreAuthorize("hasAuthority('PRODUCT_LIST')")
@Transactional(propagation = Propagation.SUPPORTS ,readOnly = true)
public PageInfo findAllProduct(Integer pageNum,Integer pageSize){
PageHelper.startPage(pageNum, pageSize);
List<Product> products = productDao.findAllProduct();
PageInfo pageInfo = new PageInfo(products);
return pageInfo;
};