本文部分内容来自黑马49期项目实战-品优购电商系统开发
文章目录
课程目标
- 实现SpringSecurity入门小Demo
- 完成运营商登陆与安全控制功能
- 完成商家入驻
- 完成商家审核
- 完成商家系统登陆与安全控制功能
Spring Security框架入门
Spring Security简介
Spring Security是一个能够基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC, DI(控制反转 Inversion of Control, DI: Dependency Injection 依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制,减少了企业系统安全控制编写大量重复代码的工作。
Spring Security入门小Demo
最简单的Demo
-
创建spring-security-demo,编辑pom.xml内容
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.huangwangxin</groupId> <artifactId>spring-security-demo</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <!-- 集中定义依赖版本号 --> <properties> <spring.version>5.0.6.RELEASE</spring.version> <spring-security.version>5.0.5.RELEASE</spring-security.version> </properties> <dependencies> <!-- Spring --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jms</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${spring.version}</version> </dependency> <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>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> <scope>provided</scope> </dependency> </dependencies> <build> <plugins> <!--java编译插件--> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.7.0</version> <configuration> <source>1.8</source> <target>1.8</target> <encoding>utf-8</encoding> </configuration> </plugin> <!-- 配置Tomcat插件--> <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <configuration> <path>/</path> <port>9090</port> </configuration> </plugin> </plugins> </build> </project>
-
创建文件
- 创建src/main/webapp/WEB-INF/web.xml
- 创建src/main/webapp/index.html
- 创建resources/spring-security.xml
-
编辑web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"> <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> </web-app>
-
编辑index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>首页</title> </head> <body> 欢迎使用Spring Security管理系统 </body> </html>
-
编辑spring-security.xml
<?xml version="1.0" encoding="UTF-8"?> <b:beans xmlns="http://www.springframework.org/schema/security" xmlns:b="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"> <!-- 页面拦截规则 use-expressions: 是否启动SPEL表达式, SPEL需要使用 hasRoles('ROLE_USER') --> <http use-expressions="false"> <intercept-url pattern="/**" access="ROLE_USER" /> <!-- 开启表单登录功能 --> <form-login /> </http> <!-- 配置密码加密 --> <b:bean id="passwordEncoder" class="org.springframework.security.crypto.password.NoOpPasswordEncoder" factory-method="getInstance"/> <!-- 认证管理器 --> <authentication-manager> <authentication-provider> <user-service> <user name="admin" password="123456" authorities="ROLE_USER"/> </user-service> <password-encoder ref="passwordEncoder"/> </authentication-provider> </authentication-manager> </b:beans>
- 配置说明:
- intercept-url表示拦截页面
/*
表示的是该目录下的资源,只包括本级目录不包括下级目录/**
表示的是该目录以及该目录下所有级别子目录的资源
- 配置说明:
-
访问http://localhost:9090/login,测试登录
用户自定义登录页
-
构建登录页
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" http-equiv="Content-Type" content="text/html"> <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>
-
构建登录构建失败页面
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>登录失败</title> </head> <body> <h1>登录失败,请重新登录</h1> </body> </html>
-
修改spring-security.xml
<?xml version="1.0" encoding="UTF-8"?> <b:beans xmlns="http://www.springframework.org/schema/security" xmlns:b="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="/login.html" security="none"></http> <http pattern="/login_fail.html" security="none"></http> <!-- 页面拦截规则 use-expressions: 是否启动SPEL表达式, SPEL需要使用 hasRoles('ROLE_USER') --> <http use-expressions="false"> <intercept-url pattern="/**" access="ROLE_USER" /> <!-- 开启表单登录功能 --> <form-login login-page="/login.html" default-target-url="/index.html" authentication-failure-url="/login_fail.html" /> <csrf disabled="true" /> </http> <!-- 配置密码加密 --> <b:bean id="passwordEncoder" class="org.springframework.security.crypto.password.NoOpPasswordEncoder" factory-method="getInstance"/> <!-- 认证管理器 --> <authentication-manager> <authentication-provider> <user-service> <user name="admin" password="123456" authorities="ROLE_USER"/> </user-service> <password-encoder ref="passwordEncoder"/> </authentication-provider> </authentication-manager> </b:beans>
-
security=‘none’ 设置此资源不被拦截
-
如果没有设置登录页 security=‘none’,会出现错误,提示太多的重定向,登录页会被反复重定向
-
login-page: 指定登录页面
-
authentication-failure-url: 指定身份验证失败时跳转的页面
-
default-target-url: 指定成功进行身份验证和授权后默认呈现给用户的页面
-
csrf disabled=‘true’ 关闭csrf,如果不加会报错
-
CSRF(Cross-site request forgery)跨站请求伪造,也被称为“One Click Attack”或者Session Riding,通常缩写为“CSRF”或者XSRF, 是一种对网站的恶意利用。
运营商系统登录与安全控制
需求分析
-
运营商登录功能
登录功能的实现
配置文件
-
修改pinyougou-manager-web的pom.xml,添加依赖
<!-- 身份验证 --> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-web</artifactId> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-config</artifactId> </dependency>
-
修改spring-security.xml
<?xml version="1.0" encoding="UTF-8"?> <b:beans xmlns="http://www.springframework.org/schema/security" xmlns:b="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="/login.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> <!-- 页面拦截规则 use-expressions: 是否启动SPEL表达式, SPEL需要使用 hasRoles('ROLE_USER') --> <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> </http> <!-- 配置密码加密 --> <b:bean id="passwordEncoder" class="org.springframework.security.crypto.password.NoOpPasswordEncoder" factory-method="getInstance"/> <!-- 认证管理器 --> <authentication-manager> <authentication-provider> <user-service> <user name="admin" password="123456" authorities="ROLE_ADMIN"/> </user-service> <password-encoder ref="passwordEncoder"/> </authentication-provider> </authentication-manager> </b:beans>
- always-use-default-target: 指定了是否在身份验证通过后总是跳转到default-target-url属性指定的url
- 如果在系统中使用了框架页面,需要设置框架页面的策略为SAMEORIGIN
-
修改web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"> <!-- 解决post乱码 --> <filter> <filter-name>CharacterEncodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>utf-8</param-value> </init-param> <init-param> <param-name>forceEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>CharacterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- 指定加载的配置文件,通过contextConfigLocation加载 --> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/springmvc.xml</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping> <!-- - Location of the XML file that defines the root application context - Applied by ContextLoaderListener. --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/spring-security.xml</param-value> </context-param> <!-- - Loads the root application context of this web app at startup. - The application context is then available via - WebApplicationContextUtils.getWebApplicationContext(servletContext). --> <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> </web-app>
登录页面
-
修改pinyougou-manager-web的login.html
<div id="profile" class="tab-pane active"> <form class="sui-form" action="/login" method="post" id="loginform"> <div class="input-prepend"><span class="add-on loginname"></span> <input name="username" id="prependedInput" type="text" placeholder="邮箱/用户名/手机号" class="span2 input-xfat"> </div> <div class="input-prepend"><span class="add-on loginpwd"></span> <input name="password" id="prependedInput" type="password" placeholder="请输入密码" class="span2 input-xfat"> </div> <div class="setting"> <div id="slider"> <div id="slider_bg"></div> <span id="label">>></span> <span id="labelTip">拖动滑块验证</span> </div> </div> <div class="logined"> <a onclick="document:loginform.submit()" class="sui-btn btn-block btn-xlarge btn-danger" target="_blank">登 录</a> </div> </form> </div>
主界面显示登录人
后端代码
-
在pinyougou-manager-web新建LoginController.java
package com.huangwangxin.manager.controller; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.HashMap; import java.util.Map; /** * 登录相关 * * @author wangxinhuang * @date 2018/7/6 */ @RestController @RequestMapping("/login") public class LoginController { @RequestMapping("/name") public Map name() { // 获取名字 String name = SecurityContextHolder.getContext().getAuthentication().getName(); Map map = new HashMap(); map.put("loginName", name); return map; } }
前端代码
-
新建loginService.js
app.service('loginService', function ($http) { this.loginName = function () { return $http.get('../login/name.do'); } });
-
新建indexController.js
app.controller('indexController', function ($scope, loginService) { // 读取当前登录人 $scope.showLoginName = function () { loginService.loginName().success( function (response) { $scope.loginName = response.loginName; } ) } });
-
修改index.html页面
-
引入JS
<script type="text/javascript" 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
<body class="hold-transition skin-green sidebar-mini" ng-app="pinyougou" ng-controller="indexController" ng-init="showLoginName()">
-
将测试用户替换成{{loginName}}
-
退出登录
-
在pinyougou-manager-web的spring-security.xml的http节点添加配置
<!-- 页面拦截规则 use-expressions: 是否启动SPEL表达式, SPEL需要使用 hasRoles('ROLE_USER') --> <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>
-
也可以自定义退出地址和跳转页面
<!-- logout-url是退出的地址,会自动生成 logout-success-url:退出后跳转的地址 --> <logout logout-url="" logout-success-url=""/>
-
index.html修改注销的链接
<div class="pull-right"> <a href="../logout" class="btn btn-default btn-flat">注销</a> </div>
商家申请入驻
需求分析
-
商家申请入驻,需要填写商家的相关的信息。
-
待运营商平台审核通过后即可使用。
准备工作
-
拷贝资源文件到pinyougou-shop-web
前端代码
-
修改register.html引入js
<script type="text/javascript" src="js/base.js"></script> <script type="text/javascript" src="plugins/angularjs/angular.min.js"></script> <script type="text/javascript" src="js/service/sellerService.js"></script> <script type="text/javascript" src="js/controller/baseController.js"></script> <script type="text/javascript" src="js/controller/sellerController.js"></script>
-
绑定表单事件
-
绑定controller
<body ng-app="pinyougou" ng-controller="sellerController">
-
绑定表单
<form class="sui-form form-horizontal"> <div class="control-group"> <label class="control-label">登陆名(不可修改):</label> <div class="controls"> <input type="text" placeholder="登陆名" class="input-xfat input-xlarge" ng-model="entity.sellerId"> </div> </div> <div class="control-group"> <label class="control-label">登陆密码:</label> <div class="controls"> <input type="password" placeholder="登陆密码" class="input-xfat input-xlarge" ng-model="entity.password"> </div> </div> <div class="control-group"> <label class="control-label">店铺名称:</label> <div class="controls"> <input type="text" placeholder="店铺名称" class="input-xfat input-xlarge" ng-model="entity.nickName"> </div> </div> <div class="control-group"> <label class="control-label">公司名称:</label> <div class="controls"> <input type="text" placeholder="公司名称" class="input-xfat input-xlarge" ng-model="entity.name"> </div> </div> <div class="control-group"> <label class="control-label">公司电话:</label> <div class="controls"> <input type="text" placeholder="公司电话" class="input-xfat input-xlarge" ng-model="entity.telephone"> </div> </div> <div class="control-group"> <label class="control-label">公司详细地址:</label> <div class="controls"> <input type="text" placeholder="公司详细地址" class="input-xfat input-xlarge" ng-model="entity.addressDetail"> </div> </div> <div class="control-group"> <label class="control-label">联系人姓名:</label> <div class="controls"> <input type="text" placeholder="联系人姓名" class="input-xfat input-xlarge" ng-model="entity.linkmanName"> </div> </div> <div class="control-group"> <label class="control-label">联系人QQ:</label> <div class="controls"> <input type="text" placeholder="联系人QQ" class="input-xfat input-xlarge" ng-model="entity.linkmanQq"> </div> </div> <div class="control-group"> <label class="control-label">联系人手机:</label> <div class="controls"> <input type="text" placeholder="联系人手机" class="input-xfat input-xlarge" ng-model="entity.linkmanMobile"> </div> </div> <div class="control-group"> <label class="control-label">联系人EMAIL:</label> <div class="controls"> <input type="text" placeholder="联系人EMAIL" class="input-xfat input-xlarge" ng-model="entity.linkmanEmail"> </div> </div> <div class="control-group"> <label class="control-label">营业执照号:</label> <div class="controls"> <input type="text" placeholder="营业执照号" class="input-xfat input-xlarge" ng-model="entity.licenseNumber"> </div> </div> <div class="control-group"> <label class="control-label">税务登记证号:</label> <div class="controls"> <input type="text" placeholder="税务登记证号" class="input-xfat input-xlarge" ng-model="entity.taxNumber"> </div> </div> <div class="control-group"> <label class="control-label">组织机构代码证:</label> <div class="controls"> <input type="text" placeholder="组织机构代码证" class="input-xfat input-xlarge" ng-model="entity.orgNumber"> </div> </div> <div class="control-group"> <label class="control-label">法定代表人:</label> <div class="controls"> <input type="text" placeholder="法定代表人" class="input-xfat input-xlarge" ng-model="entity.legalPerson"> </div> </div> <div class="control-group"> <label class="control-label">法定代表人身份证号:</label> <div class="controls"> <input type="text" placeholder="法定代表人身份证号" class="input-xfat input-xlarge" ng-model="entity.legalPersonCardId"> </div> </div> <div class="control-group"> <label class="control-label">开户行名称:</label> <div class="controls"> <input type="text" placeholder="开户行名称" class="input-xfat input-xlarge" ng-model="entity.bankName"> </div> </div> <div class="control-group"> <label class="control-label">开户行支行:</label> <div class="controls"> <input type="text" placeholder="开户行支行" class="input-xfat input-xlarge" ng-model="entity.bankName"> </div> </div> <div class="control-group"> <label class="control-label">银行账号:</label> <div class="controls"> <input type="text" placeholder="银行账号" class="input-xfat input-xlarge" ng-model="entity.bankUser"> </div> </div> <div class="control-group"> <label for="inputPassword" class="control-label"> </label> <div class="controls"> <input name="m1" type="checkbox" value="2" checked=""><span>同意协议并注册 <a href="sampling.html">《品优购商家入驻协议》</a></span> </div> </div> <div class="control-group"> <label class="control-label"></label> <div class="controls btn-reg"> <a class="sui-btn btn-block btn-xlarge btn-danger" ng-click="add()" target="_blank">申请入驻</a> </div> </div> </form>
-
-
修改sellerController.js,保存成功后跳转登录页
// 保存 $scope.add = function () { sellerService.add($scope.entity).success( function (response) { if(response.success){ // 跳转 location.href='shoplogin.html'; }else{ alert(response.message); } } ) };
后端代码
-
修改pinyougou-sellergoods-service的SellerServiceImpl类的add方法,设置默认状态为0
/** * 增加 */ @Override public void add(TbSeller seller) { seller.setStatus("0"); seller.setCreateTime(new Date()); sellerMapper.insert(seller); }
商家审核
需求分析
- 商家申请入驻后,需要网站运营人员在运营商后台进行审核,审核后商家才可以登录系统。
- 状态值:
- 0 - 未审核
- 1 - 已审核
- 2 - 审核未通过
- 3 - 关闭
商家待审核列表
-
修改pinyougou-manager-web的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/baseController.js"></script> <script type="text/javascript" src="../js/controller/sellerController.js"></script>
-
指令
<body class="hold-transition skin-red sidebar-mini" ng-app="pinyougou" ng-controller="sellerController" ng-init="searchEntity={status:'0'}">
-
加载数据
<!--数据列表--> <table id="dataList" class="table table-bordered table-striped table-hover dataTable"> <thead> <tr> <th class="" style="padding-right:0px"> <input id="selall" type="checkbox" class="icheckbox_square-blue"> </th> <th class="sorting_asc">商家ID</th> <th class="sorting">公司名称</th> <th class="sorting">店铺名称</th> <th class="sorting">联系人姓名</th> <th class="sorting">公司电话</th> <th class="text-center">操作</th> </tr> </thead> <tbody> <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> <td class="text-center"> <button type="button" class="btn bg-olive btn-xs" data-toggle="modal" data-target="#sellerModal">详情 </button> </td> </tr> </tbody> </table> <!--数据列表/--> <!-- 分页 --> <tm-pagination conf="paginationConf"></tm-pagination>
商家详情
需求分析
-
查看商家详情
前端代码
-
绑定页面弹出窗口
<!-- 商家详情 --> <div class="modal fade" id="sellerModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true"> <div class="modal-dialog modal-lg"> <div class="modal-content"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> <h3 id="myModalLabel">商家详情</h3> </div> <div class="modal-body"> <ul class="nav nav-tabs"> <li class="active"><a href="#home" data-toggle="tab">基本信息</a></li> <li><a href="#linkman" data-toggle="tab">联系人</a></li> <li><a href="#certificate" data-toggle="tab">证件</a></li> <li><a href="#ceo" data-toggle="tab">法定代表人</a></li> <li><a href="#bank" data-toggle="tab">开户行</a></li> </ul> <!-- 选项卡开始 --> <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.mobile}}</td> </tr> <tr> <td>公司电话</td> <td>{{entity.telephone}}</td> </tr> <tr> <td>公司详细地址</td> <td>{{entity.addressDetail}}</td> </tr> </table> </div> <div class="tab-pane fade" id="linkman"> <br> <table class="table table-bordered table-striped"> <tr> <td>联系人姓名</td> <td>{{entity.linkmanName}}</td> </tr> <tr> <td>联系人QQ</td> <td>{{entity.linkmanQq}}</td> </tr> <tr> <td>联系人手机</td> <td>{{entity.linkmanMobile}}</td> </tr> <tr> <td>联系人E-Mail</td> <td>{{entity.linkmanEmail}}</td> </tr> </table> </div> <div class="tab-pane fade" id="certificate"> <br> <table class="table table-bordered table-striped"> <tr> <td>营业执照号</td> <td>{{entity.licenseNumber}}</td> </tr> <tr> <td>税务登记证号</td> <td>{{entity.taxNumber}}</td> </tr> <tr> <td>组织机构代码证号</td> <td>{{entity.orgNumber}}</td> </tr> </table> </div> <div class="tab-pane fade" id="ceo"> <br> <table class="table table-bordered table-striped"> <tr> <td>法定代表人</td> <td>{{entity.legalPerson}}</td> </tr> <tr> <td>法定代表人身份证号</td> <td>{{entity.legalPersonCardId}}</td> </tr> </table> </div> <div class="tab-pane fade" id="bank"> <br> <table class="table table-bordered table-striped"> <tr> <td>开户行名称</td> <td>{{entity.bankName}}</td> </tr> <tr> <td>开户行支行</td> <td>{{entity.bankName}}</td> </tr> <tr> <td>银行账号</td> <td>{{entity.bankUser}}</td> </tr> </table> </div> </div> <!-- 选项卡结束 --> </div> <div class="modal-footer"> <button class="btn btn-success" data-dismiss="modal" aria-hidden="true">审核通过</button> <button class="btn btn-danger" data-dismiss="modal" aria-hidden="true">审核未通过</button> <button class="btn btn-danger" data-dismiss="modal" aria-hidden="true">关闭商家</button> <button class="btn btn-default" data-dismiss="modal" aria-hidden="true">关闭</button> </div> </div> </div> </div>
-
修改“详情”按钮
<button type="button" class="btn bg-olive btn-xs" data-toggle="modal" data-target="#sellerModal" ng-click="findOne(entity.sellerId)">详情 </button>
商家审核
后端代码
-
在pinyougou-sellergoods-interface工程的SellerService.java服务接口新增方法定义
/** * 更新状态 * * @param sellerId the seller id * @param status the status */ void updateStatus(String sellerId, String status);
-
在pinyougou-sellergoods–service的SellerServiceImpl.java新增方法
@Override public void updateStatus(String sellerId, String status) { TbSeller seller = sellerMapper.selectByPrimaryKey(sellerId); seller.setStatus(status); sellerMapper.updateByPrimaryKey(seller); }
-
在pinyougou-manager-web的SellerController.java新增
/** * 更新状态. * * @param sellerId the seller id * @param status the status * @return the result */ @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, "更新失败"); }
前端代码
-
修改pinyougou-manager-web的sellerService.js
// 更新状态 this.updateStatus = function (sellerId, status) { return $http.get('../seller/updateStatus.do?sellerId=' + sellerId + "&status=" + status); }
-
修改pinyougou-manager-web的sellerController.js
$scope.updateStatus=function(sellerId, status) { sellerService.updateStatus(sellerId, status).success( function (response) { if (response.success) { $scope.reloadList(); } else { alert("失败"); } } ) }
-
修改审核按钮
<div class="modal-footer"> <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> <button class="btn btn-default" data-dismiss="modal" aria-hidden="true" >关闭</button> </div>
商家系统登录与安全控制
需求分析
- 完成商家系统登录与安全控制,商家账号来自数据库,并实现密码加密
自定义认证类
-
修改pinyougou-shop-web工程的pom.xml,新增依赖
<!-- 身份验证 --> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-web</artifactId> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-config</artifactId> </dependency>
-
拷贝pinyougou-manager-web工程的spring-security.xml到pinyougou-shop-web下
<?xml version="1.0" encoding="UTF-8"?> <b:beans xmlns="http://www.springframework.org/schema/security" xmlns:b="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> <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/add.do" security="none"></http> <!-- 页面拦截规则 use-expressions: 是否启动SPEL表达式, SPEL需要使用 hasRoles('ROLE_USER') --> <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-url是退出的地址,会自动生成 logout-success-url:退出后跳转的地址 --> <logout /> </http> <!-- 配置密码加密 --> <b:bean id="passwordEncoder" class="org.springframework.security.crypto.password.NoOpPasswordEncoder" factory-method="getInstance"/> <!-- 用户服务 --> <b:bean id="userDetailService" class="com.huangwangxin.shop.service.UserDetailsServiceImpl"></b:bean> <!-- 认证管理器 --> <authentication-manager> <authentication-provider user-service-ref="userDetailService"> <password-encoder ref="passwordEncoder"/> </authentication-provider> </authentication-manager> </b:beans>
-
修改pinyougou-shop-web下的web.xml
<!-- - Location of the XML file that defines the root application context - Applied by ContextLoaderListener. --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/spring-security.xml</param-value> </context-param> <!-- - Loads the root application context of this web app at startup. - The application context is then available via - WebApplicationContextUtils.getWebApplicationContext(servletContext). --> <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>
-
修改shoplogin.html
<form class="sui-form" action="/login" method="post" id="loginform"> <div class="input-prepend"><span class="add-on loginname"></span> <input id="prependedInput" type="text" placeholder="邮箱/用户名/手机号" class="span2 input-xfat" name="username"> </div> <div class="input-prepend"><span class="add-on loginpwd"></span> <input id="prependedInput" type="password" placeholder="请输入密码" class="span2 input-xfat" name="password"> </div> <div class="setting"> <label class="checkbox inline"><input name="m1" type="checkbox" value="2" checked="">自动登录</label> <span class="forget">忘记密码?</span> </div> <div class="logined"> <a class="sui-btn btn-block btn-xlarge btn-danger" onclick="document:loginform.submit()" target="_blank">登 录</a> </div> </form>
-
在pinyougou-shop-web下创建UserDetailsServiceImpl.java类
package com.huangwangxin.shop.service; 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 java.util.ArrayList; import java.util.List; /** * 认证类 * * @author wangxinhuang * @date 2018/7/7 */ public class UserDetailsServiceImpl implements UserDetailsService { @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { List<GrantedAuthority> grantedAuthorities = new ArrayList<GrantedAuthority>(); grantedAuthorities.add(new SimpleGrantedAuthority("ROLE_SELLER")); return new User(username, "123456", grantedAuthorities); } }
认证类调用服务类方法
-
修改UserDetailsServiceImpl.java
package com.huangwangxin.shop.service; import com.huangwangxin.pojo.TbSeller; import com.huangwangxin.service.SellerService; 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 java.util.ArrayList; import java.util.List; /** * 认证类 * * @author wangxinhuang * @date 2018/7/7 */ public class UserDetailsServiceImpl implements UserDetailsService { private SellerService sellerService; public void setSellerService(SellerService sellerService) { this.sellerService = sellerService; } @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { // 构建角色列表 List<GrantedAuthority> grantedAuthorities = new ArrayList<GrantedAuthority>(); grantedAuthorities.add(new SimpleGrantedAuthority("ROLE_SELLER")); // 得到商家对象 TbSeller seller = sellerService.findOne(username); if (seller != null) { // 判断seller是否可用 if (seller.getStatus().equals("1")) { return new User(username, seller.getPassword(), grantedAuthorities) } } return null; } }
-
修改spring-security.xml
<?xml version="1.0" encoding="UTF-8"?> <b:beans xmlns="http://www.springframework.org/schema/security" xmlns:b="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.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd"> <!-- 以下页面不被拦截 --> <http pattern="/*.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/add.do" security="none"></http> <!-- 页面拦截规则 use-expressions: 是否启动SPEL表达式, SPEL需要使用 hasRoles('ROLE_USER') --> <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-url是退出的地址,会自动生成 logout-success-url:退出后跳转的地址 --> <logout /> </http> <!-- 配置密码加密 --> <b:bean id="passwordEncoder" class="org.springframework.security.crypto.password.NoOpPasswordEncoder" factory-method="getInstance"/> <!-- 用户服务 --> <b:bean id="userDetailService" class="com.huangwangxin.shop.service.UserDetailsServiceImpl"> <b:property name="sellerService" ref="sellerService"></b:property> </b:bean> <!-- 认证管理器 --> <authentication-manager> <authentication-provider user-service-ref="userDetailService"> <password-encoder ref="passwordEncoder"/> </authentication-provider> </authentication-manager> <!-- 引用dubbo服务 --> <dubbo:application name="pinyougou-shop-web" /> <dubbo:registry address="zookeeper://192.168.31.144:2181" /> <dubbo:annotation package="com.huangwangxin.shop.controller" /> <dubbo:reference id="sellerService" interface="com.huangwangxin.service.SellerService"></dubbo:reference> </b:beans>
密码加密
BCrypt加密算法
用户表的密码通常使用MD5等不可逆算法加密后存储,为防止彩虹表破解,会先用一个特定的字符串(如域名)加密,然后再使用一个随机的salt(盐值)加密。
特定字符串是程序代码中固定的,salt是每个密码单独随机。BCrypt算法将salt随机并混入最终加密后的密码,验证时也无需单独提供之前的salt,从而无需单独处理salt问题。
商家入驻密码加密
-
商家申请入驻的密码要使用BCrypt算法进行加密存储,修改SellerController.java
/** * 增加 * @param seller * @return */ @RequestMapping("/add") public Result add(@RequestBody TbSeller seller){ // 密码加密 BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); String password = passwordEncoder.encode(seller.getPassword()); seller.setPassword(password); try { sellerService.add(seller); return new Result(true, "增加成功"); } catch (Exception e) { e.printStackTrace(); } return new Result(false, "增加失败"); }
-
修改spring-security.xml
<!-- 配置密码加密 --> <b:bean id="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>
测试
-
登录
-
商家管理后台