品优购_day04
1. Spring Security框架
1.1 Spring Security简介
Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反转Inversion of Control ,DI:Dependency Injection 依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。
1.2 spring-security.xml
<!--配置不拦截的资源-->
<security:http pattern="/login.html" security="none"></security:http>
<security:http pattern="/css/**" security="none"></security:http>
<security:http pattern="/img/**" security="none"></security:http>
<security:http pattern="/js/**" security="none"></security:http>
<security:http pattern="/plugins/**" security="none"></security:http>
<!--页面拦截规则-->
<security:http use-expressions="false">
<!--配置具体的拦截规则-->
<security:intercept-url pattern="/**" access="ROLE_SELLER"/>
<!--定义跳转的具体页面-->
<security:form-login login-page="/login.html"
login-processing-url="/login.do"
default-target-url="/admin/index.html"
authentication-failure-url="/login.html"
always-use-default-target="true"/>
<!--关闭跨域请求-->
<security:csrf disabled="true"/>
<!--显示框架页-->
<security:headers>
<security:frame-options policy="SAMEORIGIN"/>
</security:headers>
<!--用户退出-->
<security:logout invalidate-session="true" logout-url="/logout.do"/>
</security:http>
<!-- 认证管理器 -->
<security:authentication-manager>
<security:authentication-provider>
<security:user-service>
<!--可直接在这里配置用户名,密码,权限-->
<security:user name="admin" password="123" authorities="ROLE_SELLER"/>
</security:user-service>
</security:authentication-provider>
</security:authentication-manager>
- intercept-url 表示拦截页面
/* 表示的是该目录下的资源,只包括本级目录不包括下级目录
/** 表示的是该目录以及该目录下所有级别子目录的资源
- form-login :为开启表单登陆;
login-processing-url:登录时提交的地址,默认为/login.do,可不写;
default-target-url:指定了成功进行身份验证和授权后默认呈现给用户的页面;
authentication-failure-url:指定了身份验证失败时跳转到的页面;
always-use-default-target:指定了是否在身份验证通过后总是跳转到default-target-url属性指定的URL。
- use-expressions 为是否使用使用 Spring 表达式语言( SpEL ),默认为ttrue ,如果开启,则拦截的配置应该写成以下形式
<intercept-url pattern="/**" access="hasRole('ROLE_USER')" />
- <security:csrf disabled="true"/>:关闭跨域请求
关闭csrf ,因为这里是html页面,如果不加会出现错误:Http status 403-Invalid CSRF token 'null' was found on the request parameter '_csrf' or header 'X-CSRF-TOKEN',如果是jsp页面,则不用关闭,要携带x-csrf-token头信息,必须是jsp页面。
CSRF(Cross-site request forgery)跨站请求伪造,也被称为“One Click Attack”或者Session Riding,通常缩写为CSRF或者XSRF,是一种对网站的恶意利用。
- <security:frame-options policy="SAMEORIGIN"/>
如果你系统中使用了框架页,如adminLTE需要设置框架页的策略为SAMEORIGIN,框架页才会显示。
- <security:logout invalidate-session="true" logout-url="/logout.do"/>
用于退出登录,添加此行,然后点击退出时访问/logout.do即可。invalidate-session:清空已定义的session ,而不是清空session里的值,logout-url:退出的地址,默认是/logout.do。
1.3 登录页面
<form id="loginForm" action="/login" method="post" >
<input name="username" type="text" placeholder="邮箱/用户名/手机号" class="span2 input-xfat">
<input name="password" type="password" placeholder="请输入密码" class="span2 input-xfat">
<a onclick="document:loginForm.submit()" target="_blank">登录</a>
</form>
1.4 显示登录用户名
可单独写一个LoginController,然后在页面初始化时访问即可。
@RestController
@RequestMapping("/login")
public class LoginController {
@RequestMapping("/name")
public Map getLoginName() {
String loginName=
SecurityContextHolder.getContext().getAuthentication().getName();
Map map=new HashMap();
map.put("loginName", loginName);
return map;
}
}
Spring Security使用一个Authentication对象来描述当前用户的相关信息。SecurityContextHolder中持有的是当前用户的SecurityContext,而SecurityContext持有的是代表当前用户相关信息的Authentication的引用,可以通过getName()或getPrincipal方法获取当前登录用户的用户名。getName()是直接获取当前线程的操作对象,框架已经封装进去, 而getPrincipal()需要自己实例化去判断,可以加入业务逻辑判断,再进行返回。
2. 商家登录与安全控制
完成商家系统登陆与安全控制,商家账号来自数据库,并实现密码加密。
2.1 自定义认证类
在pinyougou-shop-web创建com.pinyougou.service包,包下创建类UserDetailsServiceImpl.java 实现UserDetailsService接口
public class UserServiceImpl implements UserDetailsService {
private SellerService ss;//不用注解方式,用配置方式注入时,必须有一个set方法
public void setSs(SellerService ss) {
this.ss = ss;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException{
//构建角色列表
List<GrantedAuthority> list=new ArrayList();
list.add(new SimpleGrantedAuthority("ROLE_SELLER"));
//到数据库中查询用户
TbSeller ts=ss.findOne(username);
if(ts!=null&&ts.getStatus().equals("1")) {
return new User(username,ts.getPassword(),list);
}
return null;
}
}
2.2 在spring-security.xml添加如下配置
重难点:引用dubbo服务获取在远程的SellerService
<!--不过滤添加用户操作-->
<security:http pattern="/seller/add.do" security="none"></security:http>
<!-- 认证管理器 -->
<!-- 切换成数据库中的用户名和密码 -->
<security:authentication-manager>
<security:authentication-provider user-service-ref="userService">
<!-- 配置加密的方式 -->
<security:password-encoder ref="bCryptPasswordEncoder"/>
</security:authentication-provider>
</security:authentication-manager>
<!--配置认证类-->
<bean id="userService" class="com.pinyougou.service.UserServiceImpl">
<property name="ss" ref="sellerService"></property>
<!--name="ss"这个名字与UserServiceImpl中的ss相同-->
</bean>
<!--
UserServiceImpl用到了SellerService,而UserServiceImpl在18pinyougou-shop-web,而SerllerService在18pinyougou-sellergoods-interface,即SellerService在远程,所以要引用dubbo服务获取-->
<!-- 引用dubbo 服务 -->
<dubbo:application name="18pinyougou-shop-web"></dubbo:application>
<dubbo:registry address="zookeeper://192.168.25.130:2181"></dubbo:registry>
<dubbo:reference id="sellerService" interface="com.pinyougou.sellergoods.service.SellerService"></dubbo:reference>
2.3 密码加密
2.3.1 BCrypt加密算法
用户表的密码通常使用MD5等不可逆算法加密后存储,为防止彩虹表破解更会先使用一个特定的字符串(如域名)加密,然后再使用一个随机的salt(盐值)加密。 特定字符串是程序代码中固定的,salt是每个密码单独随机,一般给用户表加一个字段单独存储,比较麻烦。 BCrypt算法将salt随机并混入最终加密后的密码,验证时也无需单独提供之前的salt,从而无需单独处理salt问题。
2.3.2 使用BCrypt进行加密存储
创建密码加密工具类
public class PasswordEncoderUtils {
private static BCryptPasswordEncoder bpe=new BCryptPasswordEncoder();
//返回加密后的密码
public static String passwordEncode(String password) {
return bpe.encode(password);
}
}
2.3.3 在spring-security.xml配置加密类
<security:authentication-manager>
......
<!-- 配置加密的方式 -->
<security:password-encoder ref="bCryptPasswordEncoder"/>
</security:authentication-provider>
</security:authentication-manager>
<!-- 配置加密类 -->
<!--声明该加密类为一个名为bCryptPasswordEncoder的bean-->
<bean id="bCryptPasswordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>