目标
- 实现springsecurity的入门小demo
- 完成运营商登录与安全控制功能
- 完成商家入驻
- 完成商家审核
- 完成商家系统登录与安全控制功能
1. 和之前的比较
1. jar包比较
ssm项目中引入的jar包
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>${spring.security.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>${spring.security.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>${spring.security.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
<version>${spring.security.version}</version>
</dependency>
本次jar包
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>4.1.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>4.1.0.RELEASE</version>
</dependency>
少了spring-security-core,maven导包时自动导入了。没用taglibs
2. web.xml
都一样
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-security.xml</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<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
ssm项目
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:security="http://www.springframework.org/schema/security"
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.xsd">
<security:global-method-security pre-post-annotations="enabled" secured-annotations="enabled"/>
<!--配置不拦截的资源-->
<security:http pattern="/login.jsp" security="none"/>
<security:http pattern="/failer.jsp" security="none"/>
<security:http pattern="/css/**" security="none"/>
<security:http pattern="/img/**" security="none"/>
<security:http pattern="/plugins/**" security="none"/>
<!--
配置具体的规则
auto-config="true" 不用自己编写登录的页面,框架提供默认登录页面
use-expressions="false" 是否使用SPEL表达式
-->
<security:http auto-config="true" use-expressions="true">
<!--配置具体的拦截的规则 pattern="请求路径的规则"
access="访问系统的人,必须有ROLE_USER的角色"-->
<security:intercept-url pattern="/**" access="hasAnyRole('ROLE_USER','ROLE_ADMIN')"/>
<!--定义跳转的具体的页面-->
<security:form-login
login-page="/login.jsp"
login-processing-url="/login.do"
default-target-url="/index.jsp"
authentication-failure-url="/failer.jsp"
authentication-success-forward-url="/pages/main.jsp"
/>
<!--关闭跨域请求-->
<security:csrf disabled="true"/>
<!--退出-->
<security:logout invalidate-session="true" logout-url="/logout.do" logout-success-url="/login.jsp"/>
</security:http>
<!--认证管理器,切换成数据库中的用户名和密码-->
<security:authentication-manager>
<security:authentication-provider user-service-ref="userService">
<!--配置加密的方式-->
<security:password-encoder ref="passwordEncoder"/>
</security:authentication-provider>
</security:authentication-manager>
<!--配置加密类-->
<!--bcrypt,是一个跨平台的文件加密工具。-->
<bean id="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>
</beans>
采用数据库的users表
对应实体类UserInfo
public class UserInfo {
private String id;
private String username;
private String email;
private String password;
private String phoneNum;
private int status; // 状态0 未开启 1 开启
private String statusStr;
private List<Role> roles;
//...
角色表role
剩余部分见之前分析。
项目中的直接看本次项目
2. 运营商系统登录与安全控制
1. 登录功能的实现
1. 配置文件
修改pinyougou-manager-web的pom.xml文件,添加依赖
修改web.xml,和之前项目一样
pinyougou-manager-web的spring目录下添加配置文件spring-security.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="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.xsd">
<!--设置页面不登录也能访问-->
<http pattern="/*.html" security="none"/>
<http pattern="/css/**" security="none"/>
<http pattern="/img/**" security="none"/>
<http pattern="/js/**" security="none"/>
<http pattern="/plugins/**" security="none"/>
<!--页面的拦截规则-->
<http use-expressions="false">
<intercept-url pattern="/**" access="ROLE_ADMIN"/>
<form-login login-page="/login.html" default-target-url="/admin/index.html"
authentication-failure-url="/login.html" always-use-default-target="true"/>
<csrf disabled="true"/>
<headers>
<frame-options policy="SAMEORIGIN"/>
</headers>
<logout/>
</http>
<!--认证管理器-->
<authentication-manager>
<authentication-provider>
<user-service>
<user name="admin" password="123" authorities="ROLE_ADMIN"/>
<user name="tom" password="123" authorities="ROLE_ADMIN"/>
</user-service>
</authentication-provider>
</authentication-manager>
</beans:beans>
always-use-default-target指定是否在身份验证通过后总是跳转到default-target-url属性指定的url
如果使用了iframe,需要设置框架页的策略为SAMEORIGIN
2. login.html
<form class="sui-form" action="/login" method="post" id="loginForm">
<div class="input-prepend"><span class="add-on loginname"></span>
<input id="prependedInput" name="username" type="text" placeholder="邮箱/用户名/手机号" class="span2 input-xfat">
</div>
<div class="input-prepend"><span class="add-on loginpwd"></span>
<input id="prependedInput" name="password" type="password" placeholder="请输入密码" class="span2 input-xfat">
</div>
<a class="sui-btn btn-block btn-xlarge btn-danger" onclick="document:loginForm.submit()" target="_blank">登 录</a>
3. 主界面显示登录user
1. 新建loginController.java
@RestController
@RequestMapping("/login")
public class LoginController {
@RequestMapping("/getName")
public Map getName(){
String name = SecurityContextHolder.getContext().getAuthentication().getName();
Map map = new HashMap();
map.put("loginName",name);
return map;
}
}
2. 新建loginService.js
// 登录服务层
app.service('loginService',function ($http) {
this.loginName = function () {
return $http.get('../login/getName.do');
}
});
3. 新建indexController.js
app.controller('indexController',function ($scope,$controller, loginService) {
// 显示当前用户名
$scope.showLoginName = function () {
loginService.loginName().success(
function (response) {
$scope.loginName = response.loginName;
}
);
}
});
4. 页面引入js
<script src="../plugins/adminLTE/js/app.min.js"></script>
<script src="../plugins/angularjs/angular.min.js"></script>
<script type="text/javascript" src="../js/base.js"></script>
<script type="text/javascript" src="../js/service/loginService.js"></script>
<script type="text/javascript" src="../js/controller/indexController.js"></script>
指令
<body class="hold-transition skin-green sidebar-mini" ng-app="pinyougou" ng-controller="indexController" ng-init="showLoginName()">
将页面的测试用户,替换为{{loginName}}
4. 退出登录
在pinyougou-manager-web的spring-security.xml的http节点天啊及配置
<logout/>
修改注销的链接
<a href="../logout" class="btn btn-default btn-flat">注销</a>
2. 商家申请入驻
1. 需求分析
需要填写商家相关的信息,运营商平台审核通过后可以使用
2. 准备工作
- 拷贝静态资源到pinyougou-shop-web工程
- 参照运营商后台构建js controller的base goods和seller;service的goods和seller
- 拷贝后端控制层代码 GoodsController和SellerController
3. 前端代码
修改register.html,引入js
<script src="plugins/angularjs/angular.min.js"></script>
<script type="text/javascript" src="js/base.js"></script>
<script type="text/javascript" src="js/service/sellerService.js"></script>
<script type="text/javascript" src="js/controller/sellerController.js"></script>
<script type="text/javascript" src="js/controller/baseController.js"></script>
指令
<body ng-app="pinyougou" ng-controller="sellerController">
绑定表单
将对应的都更改
<input type="text" placeholder="登陆名" ng-model="entity.sellerId" class="input-xfat input-xlarge">
...
修改sellerController.js,保存成功后跳转到登录页
//新增
$scope.add=function(){
sellerService.add($scope.entity).success(
function(response){
if(response.success){
//如果注册成功,跳转登录页面
location.href="shoplogin.html";
}else{
alert(response.message);
}
}
);
}
绑定“申请入驻”按钮
<a class="sui-btn btn-block btn-xlarge btn-danger" ng-click="add()" target="_blank">申请入驻</a>
4. 后端代码
修改后端SellerServiceImpl的add方法,设置状态为0
@Override
public void add(TbSeller seller) {
seller.setCreateTime(new Date());// 申请日期
seller.setStatus("0");// 状态
sellerMapper.insert(seller);
}
3. 商家审核
1. 需求分析
网站运营人员在运营商后台进行审核,审核后商家才能登录系统
状态包括:0 未审核 1 已审核 2 审核未通过 3 关闭
2. 商家待审核列表
修改seller_1.html,引入js
<script src="../plugins/angularjs/angular.min.js"></script>
<!--分页组件开始-->
<script src="../plugins/angularjs/pagination.js"></script>
<link rel="stylesheet" href="../plugins/angularjs/pagination.css">
<!--分页组件结束-->
<script type="text/javascript" src="../js/base_pagination.js"></script>
<script type="text/javascript" src="../js/service/sellerService.js"></script>
<script type="text/javascript" src="../js/controller/sellerController.js"></script>
<script type="text/javascript" src="../js/controller/baseController.js"></script>
指令
<body class="hold-transition skin-red sidebar-mini" ng-app="pinyougou" ng-controller="sellerController" ng-init="searchEntity={status:'0'}">
加入分页控件
<tm-pagination conf="paginationConf"></tm-pagination>
循环
<tr ng-repeat="entity in list">
<td><input type="checkbox"></td>
<td>{{entity.sellerId}}</td>
<td>{{entity.name}}</td>
<td>{{entity.nickName}}</td>
<td>{{entity.linkmanName}}</td>
<td>{{entity.telephone}}</td>
3. 商家详情
1. 绑定页面弹出窗口
<!-- 选项卡开始 -->
<div id="myTabContent" class="tab-content">
<div class="tab-pane active in" id="home">
<br>
<table class="table table-bordered table-striped" width="800px">
<tr>
<td>公司名称</td>
<td>{{entity.name}}</td>
</tr>
<tr>
<td>店铺名称</td>
<td>{{entity.nickName}}</td>
...
2. 绑定详情按钮
<button type="button" class="btn bg-olive btn-xs" data-toggle="modal" data-target="#sellerModal" ng-click="findOne(entity.sellerId)">详情</button>
4. 商家审核
1. 后端代码
seller-goods-interface工程的SellerService.java新增方法
public void updateStatus(String sellerId, String status);
sellergoods-service的SellerServiceImpl.java新增方法
@Override
public void updateStatus(String sellerId, String status) {
TbSeller seller = sellerMapper.selectByPrimaryKey(sellerId);
seller.setStatus(status);
sellerMapper.updateByPrimaryKey(seller);
}
manager-web的SellerController.java新增方法
@RequestMapping("/updateStatus")
public Result updateStatus(String sellerId, String status){
try {
sellerService.updateStatus(sellerId,status);
return new Result(true,"成功");
} catch (Exception e) {
e.printStackTrace();
return new Result(false,"失败");
}
}
2. 前端代码
修改manager-web的sellerService.js
// 更新状态
this.updateStatus = function (sellerId,status) {
return $http.get('../seller/updateStatus.do?sellerId='+sellerId+'&status='+status);
}
修改selllerController.js
// 更改状态
$scope.updateStatus = function (sellerId, status) {
sellerService.updateStatus(sellerId, status).success(
function (response) {
if(response.success){
$scope.reloadList();
}else{
alert(response.message);
}
}
);
}
修改按钮,调用方法
<button class="btn btn-success" data-dismiss="modal" aria-hidden="true" ng-click="updateStatus(entity.sellerId,'1')">审核通过</button>
<button class="btn btn-danger" data-dismiss="modal" aria-hidden="true" ng-click="updateStatus(entity.sellerId,'2')">审核未通过</button>
<button class="btn btn-danger" data-dismiss="modal" aria-hidden="true" ng-click="updateStatus(entity.sellerId,'3')">关闭商家</button>
3. 商家系统登录和安全控制
1. 需求分析
完成商家系统登录和安全控制,商家账号来自数据库,实现密码加密
2. 自定义认证类
1. 配置文件
pom.xml、web.xml、shoplogin.html参照运营商管理后台
2. UserDetailsServiceImpl
在shop-web下创建com.pinyougou.service包,包下创建类UserDetailsServiceImpl.java实现UserDetailsService接口
注意最好不要用注解,set方法注入sellerService
public class UserDetailsServiceImpl implements UserDetailsService {
private SellerService sellerService;
public void setSellerService(SellerService sellerService) {
this.sellerService = sellerService;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
System.out.println("经过了UserDetailsServiceImpl");
// 构建角色列表
List<GrantedAuthority> grantAuths = new ArrayList<>();
grantAuths.add(new SimpleGrantedAuthority("ROLE_SELLER"));
// 得到商家对象
TbSeller seller = sellerService.findOne(username);
if(seller!=null){
if(seller.getStatus().equals("1")){
return new User(username,seller.getPassword(),grantAuths);
}else{
return null;
}
}else{
return null;
}
}
}
3. 创建spring-security.xml
模仿运营商部分
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xmlns:beans="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://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd">
<!--设置页面不登录也能访问-->
<http pattern="/*.html" security="none"/>
<http pattern="/css/**" security="none"/>
<http pattern="/img/**" security="none"/>
<http pattern="/js/**" security="none"/>
<http pattern="/plugins/**" security="none"/>
<http pattern="/seller/add.do" security="none"/>
<!--页面的拦截规则-->
<http use-expressions="false">
<intercept-url pattern="/**" access="ROLE_SELLER"/>
<form-login login-page="/shoplogin.html" default-target-url="/admin/index.html"
authentication-failure-url="/shoplogin.html" always-use-default-target="true"/>
<csrf disabled="true"/>
<headers>
<frame-options policy="SAMEORIGIN"/>
</headers>
<logout/>
</http>
<!--认证管理器-->
<authentication-manager>
<authentication-provider user-service-ref="userDetailService">
<password-encoder ref="bCryptPasswordEncoder"/>
</authentication-provider>
</authentication-manager>
<!--认证类-->
<beans:bean id="userDetailService" class="com.pinyougou.service.UserDetailsServiceImpl">
<beans:property name="sellerService" ref="sellerService"/>
</beans:bean>
<!-- 引用dubbo 服务 -->
<dubbo:application name="pinyougou-shop-web" />
<dubbo:registry address="zookeeper://192.168.25.128:2181"/>
<dubbo:reference interface="com.pinyougou.sellergoods.service.SellerService" id="sellerService"/>
<beans:bean id="bCryptPasswordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>
</beans:beans>
3. 认证类调用服务方法
修改UserDetailsServiceImpl.java,添加属性和setter方法,修改loadUserByUsername方法
调用SellerService
修改shop-web的spring-security.xml,添加配置 dubbo的sellerService
4. 密码加密
BCrypt算法加盐,防止MD5被彩虹表破解
1. 商家入驻密码加密
修改SellerController.java的add方法
@RequestMapping("/add")
public Result add(@RequestBody TbSeller seller){
// 密码加密
BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
String password = bCryptPasswordEncoder.encode(seller.getPassword());//加密
seller.setPassword(password);
try {
sellerService.add(seller);
return new Result(true, "增加成功");
} catch (Exception e) {
e.printStackTrace();
return new Result(false, "增加失败");
}
}
2. 加密配置
修改spring-security.xml,添加配置
bcryptEncoder的bean以及将其配置到认证管理器
5. 显示登录名
同运营商
6. 退出登录
同运营商