品优购 地4天
学习目标
目标1:实现SpringSecurity入门小Demo
目标2:完成运营商登陆与安全控制功能
目标3:完成商家入驻
目标4:完成商家审核
目标5:完成商家系统登陆与安全控制功能
目标6:完成商家异步登录
第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入门小Demo
1.2.1 SpringSecurity入门Demo
1.2.1.1 创建工程spring-security-demo1 ,pom.xml内容
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.itheima</groupId>
<artifactId>springsecurity-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<spring.security.version>4.2.4.RELEASE</spring.security.version>
<servlet-api.version>3.0.1</servlet-api.version>
<jstl.version>1.2</jstl.version>
<commons-logging.version>1.2</commons-logging.version>
</properties>
<dependencies>
<!--日志包-->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>${commons-logging.version}</version>
</dependency>
<!--spring-security-->
<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-cas</artifactId>
<version>${spring.security.version}</version>
</dependency>
<!--JSP-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>${servlet-api.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>${jstl.version}</version>
</dependency>
</dependencies>
</project>
1.2.1.2 创建web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<!--SpringSecurity过滤器-->
<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>
<!--指定Spring配置文件-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring.xml</param-value>
</context-param>
<!--Spring监听-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
1.2.1.3 创建html和jsp
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
第一个SpringSecurity应用
</body>
</html>
error.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>异常处理</title>
</head>
<body>
您无权访问该页面
</body>
</html>
login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<body>
<form name='f' action='/login' method='POST'>
<table>
<tr><td>User:</td><td><input type='text' name='username' value=''></td></tr>
<tr><td>Password:</td><td><input type='password' name='password'/></td></tr>
<tr><td colspan='2'><input name="submit" type="submit" value="Login"/></td></tr>
</table>
</form>
</body>
</html>
创建pages/1.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
pages下的jsp
</body>
</html>
创建jsp/1.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
jsp下的jsp
</body>
</html>
1.2.1.4 创建spring配置文件 spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--包扫描-->
<context:component-scan base-package="com" />
</beans>
1.2.1.5 创建SpringSecurityConfig配置类
该类需要继承WebSecurityConfigurerAdapter,并且该类需要加上@EnableWebSecurity注解,该类里面通常要写3个方法:
忽略某些配置的方法
public void configure(WebSecurity web) throws Exception {}
配置对应地址拦截请求的方法,例如拦截地址、关闭csrf、
protected void configure(HttpSecurity http) throws Exception {}
授权用户,比如创建某些账号,某些账号就可以登录了
protected void configure(AuthenticationManagerBuilder auth) throws Exception {}
实现代码:
@Component
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
/***
* 忽略安全过滤
* @param web
* @throws Exception
*/
@Override
public void configure(WebSecurity web) throws Exception {
//忽略相关地址
web.ignoring().antMatchers("/images/**");
web.ignoring().antMatchers("/js/**");
web.ignoring().antMatchers("/login.html");
web.ignoring().antMatchers("/error.html");
}
/***
* 请求拦截配置
* @param http
* @throws Exception
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
//拦截规则配置
http.authorizeRequests()
.antMatchers("/pages/**").access("hasRole('ADMIN')") //ADMIN角色可以访问/pages/下的所有文件
.antMatchers("/jsp/**").access("hasRole('USER')") //USER角色可以访问/jsp/下的所有文件
.and().formLogin().loginPage("/login.html").loginProcessingUrl("/login") //指定登录页和处理登录的地址
.and().logout().logoutUrl("/logout").invalidateHttpSession(true); //指定登出页和登出后让session无效
//关闭CSRF安全策略
http.csrf().disable();
//异常处理,例如403
http.exceptionHandling().accessDeniedPage("/error.html");
//只允许一个用户登录,如果同一个账户两次登录,那么第一个账户将被踢下线,跳转到登录页面
http.sessionManagement().maximumSessions(1).expiredUrl("/login.html");
}
/***
* 创建用户并授权
* @param auth
* @throws Exception
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//创建一个用户存在内存中,账号是admin,密码是123456,角色是ROLE_ADMIN
auth.inMemoryAuthentication().withUser("admin").password("123456").roles("ADMIN");
auth.inMemoryAuthentication().withUser("xiaohong").password("123456").roles("USER");
}
}
1.2.1.6 自定义认证类
上面的账号密码写死了,很多时候我们需要自己写认证类,代码如下:
@Component
public class UserDetailsServiceImpl implements UserDetailsService {
/***
* 自定义认证类
* @param username
* @return
* @throws UsernameNotFoundException
*/
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//根据用户名去数据库中查询用户信息,这里不做演示,模拟已经查询出结果
String password="123456"; //密码
//用户授权信息
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
authorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
return new User(username,password,authorities);
}
}
修改SpringSecurityConfig配置类
/***
* UserDetailsServiceImpl是UserDetailsService的实现类,这里可以直接注入进来
*/
@Autowired
private UserDetailsService userDetailsService;
/***
* 创建用户并授权
* @param auth
* @throws Exception
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//创建一个用户存在内存中,账号是admin,密码是123456,角色是ROLE_ADMIN
//auth.inMemoryAuthentication().withUser("admin").password("123456").roles("ADMIN");
//auth.inMemoryAuthentication().withUser("xiaohong").password("123456").roles("USER");
//注册自定义认证类
auth.userDetailsService(userDetailsService);
}
第2章 运营商系统登录与安全控制
2.1 需求分析
完成运营商登陆功能
2.2 登陆功能的实现
2.2.1 配置文件
2.2.1.1 修改pinyougou-manager-web的pom.xml ,添加依赖
<!--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>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-cas</artifactId>
</dependency>
<!--JSP依赖-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
</dependency>
2.2.1.2 修改web.xml
在web.xml中添加SpringSecurity过滤器
<!--SpringSecurity过滤器-->
<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>
出现这种请求,注意说明当前SpringSecurity配置没有被读取或者web.xml没有配置该过滤器,沿着这2个方向查找。
2.2.1.3 创建SpringSecurity配置类
创建SpringSecurityConfig.java类,该类继承WebSecurityConfigurerAdapter,完整代码如下:
@Component
@EnableWebSecurity
public class SecurityHandler extends WebSecurityConfigurerAdapter {
/***
* 公共资源取消安全校验
* @param web
* @throws Exception
*/
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/css/**");
web.ignoring().antMatchers("/img/**");
web.ignoring().antMatchers("/js/**");
web.ignoring().antMatchers("/plugins/**");
web.ignoring().antMatchers("/login.html");
web.ignoring().antMatchers("/error.html");
}
/****
* 安全校验配置
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
//其他任何路径都需要管理员登录
http.authorizeRequests().antMatchers("/**").access("hasRole('ADMIN')");
//登录相关配置
http.formLogin().loginPage("/login.html") //指定登录地址
.loginProcessingUrl("/login") //指定处理登录的请求地址
.defaultSuccessUrl("/admin/index.html",true); //登录成功后总是跳转到/admin/index.html页面
//登出配置
http.logout().logoutUrl("/logout").invalidateHttpSession(true); //登出地址为/logout,并且登出后销毁session
//设置用户只允许在一处登录,在其他地方登录则挤掉已登录用户,被挤掉的已登录用户则需要返回/login.html重新登录
http.sessionManagement().maximumSessions(1).expiredUrl("/login.html");
//关闭CSRF安全策略
http.csrf().disable();
//允许跳转显示iframe
http.headers().frameOptions().disable();
//异常处理页面,例如没有权限访问等
http.exceptionHandling().accessDeniedPage("/error.html");
}
/****
* 用户授权信息
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//这里给一个固定账号模拟,后面商家登录我们就从数据库查询
auth.inMemoryAuthentication().withUser("admin").password("123456").roles("ADMIN");
}
}
2.2.1.4 登录页面
修改pinyougou-manager-web的 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>
<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 class="sui-btn btn-block btn-xlarge btn-danger" onclick="document:loginform.submit()" >登 录</a>
</div>
</form>
2.2 显示用户名
2.2.1 后端代码
在pinyougou-manager-web新建LoginController.java
package com.pinyougou.manager.controller;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import sun.plugin.liveconnect.SecurityContextHelper;
@RestController
@RequestMapping(value = "/login")
public class LoginController {
/***
* 获取用户登录名字
* @return
*/
@RequestMapping(value = "/name")
public String getUserName(){
//获取登录的用户名
return SecurityContextHolder.getContext().getAuthentication().getName();
}
}
2.2.2 前段代码
新建loginService.js
app.service("loginService",function($http){
//查询用户登录名字信息
this.getLoginUserName=function(){
return $http.post("/login/name.shtml");
}
});
新建loginController.js
app.controller("loginController",function($scope,$http,$controller,loginService){
//获取用户名
$scope.getUserName=function(){
loginService.getLoginUserName().success(function(response){
$scope.userloginname=response;
});
}
});
页面引入依赖文件
<!--引用-->
<script src="/plugins/angularjs/angular.min.js"></script>
<script src="/js/base.js"></script>
<script src="/js/service/loginService.js"></script>
<script src="/js/controller/loginController.js"></script>
添加控制指令
<body ng-app="pinyougou" ng-controller="loginController" ng-init="getUserName()" class="hold-transition skin-green sidebar-mini" >
将页面上的测试用户 替换成 {{userloginname}}
测试效果如下:
2.3 退出登录
在pinyougou-manager-web中修改admin/index.html,修改注销的链接
<a href="/logout" class="btn btn-default btn-flat">注销</a>
第3章 商家申请入驻
3.1 需求分析
商家申请入驻,需要填写商家相关的信息。待运营商平台审核通过后即可使用使用。
3.2 准备工作
3.2.1 创建商家后台项目
3.2.1.1 pinyougou-shop-web
3.2.1.2 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">
<parent>
<artifactId>pinyougou-parent</artifactId>
<groupId>com.pinyougou</groupId>
<version>1.0-SNAPSHOT</version>
<relativePath>../pinyougou-parent/pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>pinyougou-shop-web</artifactId>
<!--打war包-->
<packaging>war</packaging>
<dependencies>
<!--依赖interface-->
<dependency>
<artifactId>pinyougou-sellergoods-interface</artifactId>
<groupId>com.pinyougou</groupId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- SpringMVC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
<!-- spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
</dependency>
<!-- Apache工具组件 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<!--FastJSON-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
<!--dubbo-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<exclusions>
<exclusion>
<artifactId>slf4j-api</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<exclusions>
<exclusion>
<artifactId>zookeeper</artifactId>
<groupId>org.apache.zookeeper</groupId>
</exclusion>
<exclusion>
<artifactId>slf4j-api</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>
<!--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>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-cas</artifactId>
</dependency>
<!--JSP-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
</dependency>
<!-- Test dependencies -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
3.2.1.3 配置web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<!-- 编码过滤器 -->
<filter>
<filter-name>characterEncoding</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>characterEncoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--//End 编码过滤器 -->
<!--springsecurity过滤器-->
<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>
<!-- SpringMVC -->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>*.shtml</url-pattern>
</servlet-mapping>
<!-- //End SpringMVC -->
</web-app>
3.2.1.4 配置springmvc配置文件
创建resources/spring/springmvc.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
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/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<!--dubbo配置-->
<!--注册应用-->
<dubbo:application name="pinyougou-shop-web" />
<!--设置客户端请求超时时间-->
<dubbo:consumer timeout="10000" />
<!--注册中心-->
<dubbo:registry address="zookeeper://192.168.211.128:2181" />
<!--包扫描-->
<dubbo:annotation package="com.pinyougou" />
<!--注解驱动-->
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<constructor-arg value="UTF-8"/>
</bean>
<bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>text/html;charset=UTF-8</value>
<value>application/json;charset=UTF-8</value>
</list>
</property>
<property name="fastJsonConfig">
<bean class="com.alibaba.fastjson.support.config.FastJsonConfig">
<property name="serializerFeatures">
<array>
<value>WriteMapNullValue</value>
<value>WriteNullStringAsEmpty</value>
</array>
</property>
<property name="charset" value="UTF-8"/>
</bean>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
<!-- 包扫描 -->
<context:component-scan base-package="com.pinyougou" />
</beans>
3.2.1.5 创建SpringSecurity核心配置类
创建SpringSecurityConfig.java类
@Component
@EnableWebSecurity
public class SecurityHandler extends WebSecurityConfigurerAdapter {
/*****
* 忽略一些公开链接的权限设置
*/
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/seller/add.shtml");
web.ignoring().antMatchers("/*.html");
web.ignoring().antMatchers("/css/**");
web.ignoring().antMatchers("/img/**");
web.ignoring().antMatchers("/js/**");
web.ignoring().antMatchers("/plugins/**");
}
/****
* 其他非公开链接的权限设置以及其他访问设置
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
//其他链接地址都需要SELLER角色
http.authorizeRequests().antMatchers("/**").access("hasRole('SELLER')");
//登录设置
http.formLogin().loginPage("/shoplogin.html") //登录跳转地址
.loginProcessingUrl("/login") //登录处理地址
.defaultSuccessUrl("/admin/index.html",true) //登录后始终跳转到后台首页
.failureForwardUrl("/shoplogin.html"); //登录失败后跳转地址
//登出设置
http.logout().invalidateHttpSession(true) //让session无效
.logoutUrl("/logout") //登出处理地址
.logoutSuccessUrl("/shoplogin.html"); //登出后跳转地址
//发生异常跳转地址
http.exceptionHandling().accessDeniedPage("/error.html");
//允许跳转iframe
http.headers().frameOptions().disable();
//关闭csrf
http.csrf().disable();
}
/****
* 用户授权
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("admin").password("123456").roles("ADMIN");
}
}
3.2.2 静态资源拷贝
3.2.2.1 拷贝静态页
拷贝资源: 将“资源/静态原型/商家管理后台”下的页面拷贝到pinyougou-shop-web工程
3.2.2.2 参照“运营商后台”构建js
3.2.2.3 拷贝后端控制层代码
3.3 前端代码
3.3.1 修改register.html 引入JS
<script src="/plugins/angularjs/angular.min.js"></script>
<!--引用-->
<script src="/js/base.js"></script>
<script src="/js/service/sellerService.js"></script>
<script src="/js/controller/baseController.js"></script>
<script src="/js/controller/sellerController.js"></script
3.3.2 页面代码绑定
指令
<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 ng-model="entity.sellerId" type="text" placeholder="登陆名" class="input-xfat input-xlarge">
</div>
</div>
<div class="control-group">
<label class="control-label">登陆密码:</label>
<div class="controls">
<input ng-model="entity.password" type="password" placeholder="登陆密码" class="input-xfat input-xlarge">
</div>
</div>
<div class="control-group">
<label class="control-label">店铺名称:</label>
<div class="controls">
<input ng-model="entity.nickName" type="text" placeholder="店铺名称" class="input-xfat input-xlarge">
</div>
</div>
<div class="control-group">
<label class="control-label">公司名称:</label>
<div class="controls">
<input ng-model="entity.name" type="text" placeholder="公司名称" class="input-xfat input-xlarge">
</div>
</div>
<div class="control-group">
<label class="control-label">公司电话:</label>
<div class="controls">
<input ng-model="entity.telephone" type="text" placeholder="公司电话" class="input-xfat input-xlarge">
</div>
</div>
<div class="control-group">
<label class="control-label">公司详细地址:</label>
<div class="controls">
<input ng-model="entity.addressDetail" type="text" placeholder="公司详细地址" class="input-xfat input-xlarge">
</div>
</div>
<div class="control-group">
<label class="control-label">联系人姓名:</label>
<div class="controls">
<input ng-model="entity.linkmanName" type="text" placeholder="联系人姓名" class="input-xfat input-xlarge">
</div>
</div>
<div class="control-group">
<label class="control-label">联系人QQ:</label>
<div class="controls">
<input ng-model="entity.linkmanQq" type="text" placeholder="联系人QQ" class="input-xfat input-xlarge">
</div>
</div>
<div class="control-group">
<label class="control-label">联系人手机:</label>
<div class="controls">
<input ng-model="entity.linkmanMobile" type="text" placeholder="联系人手机" class="input-xfat input-xlarge">
</div>
</div>
<div class="control-group">
<label class="control-label">联系人EMAIL:</label>
<div class="controls">
<input ng-model="entity.linkmanEmail" type="text" placeholder="联系人EMAIL" class="input-xfat input-xlarge">
</div>
</div>
<div class="control-group">
<label class="control-label">营业执照号:</label>
<div class="controls">
<input ng-model="entity.licenseNumber" type="text" placeholder="营业执照号" class="input-xfat input-xlarge">
</div>
</div>
<div class="control-group">
<label class="control-label">税务登记证号:</label>
<div class="controls">
<input ng-model="entity.taxNumber" type="text" placeholder="税务登记证号" class="input-xfat input-xlarge">
</div>
</div>
<div class="control-group">
<label class="control-label">组织机构代码证:</label>
<div class="controls">
<input ng-model="entity.orgNumber" type="text" placeholder="组织机构代码证" class="input-xfat input-xlarge">
</div>
</div>
<div class="control-group">
<label class="control-label">法定代表人:</label>
<div class="controls">
<input ng-model="entity.legalPerson" type="text" placeholder="法定代表人" class="input-xfat input-xlarge">
</div>
</div>
<div class="control-group">
<label class="control-label">法定代表人身份证号:</label>
<div class="controls">
<input ng-model="entity.legalPersonCardId" type="text" placeholder="法定代表人身份证号" class="input-xfat input-xlarge">
</div>
</div>
<div class="control-group">
<label class="control-label">开户行名称:</label>
<div class="controls">
<input ng-model="entity.bankUser" type="text" placeholder="开户行名称" class="input-xfat input-xlarge">
</div>
</div>
<div class="control-group">
<label class="control-label">开户行支行:</label>
<div class="controls">
<input ng-model="entity.bankName" type="text" placeholder="开户行支行" class="input-xfat input-xlarge">
</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" target="_blank">申请入驻</a>
</div>
</div>
</form>
3.3.3 修改js
修改sellerController.js ,在保存成功后跳转到登陆页
//添加或者修改方法
$scope.save = function(){
var result = null;
if($scope.entity.id!=null){
//执行修改数据
result = sellerService.update($scope.entity);
}else{
//增加操作
result = sellerService.add($scope.entity);
}
//判断操作流程
result.success(function(response){
//判断执行状态
if(response.success){
//重新加载新的数据
location.href='/shoplogin.html';
}else{
//打印错误消息
alert(response.message);
}
});
}
绑定“申请入驻”按钮
<a ng-click="save()" class="sui-btn btn-block btn-xlarge btn-danger" target="_blank">申请入驻</a>
3.4 后端代码
修改后端代码pinyougou-sellergoods-service的SellerServiceImpl类的add方法,设置默认状态为0
/***
* 增加Seller信息
* @param seller
* @return
*/
@Override
public int add(Seller seller) {
//设置默认状态和创建时间
seller.setStatus("0");
seller.setCreateTime(new Date());
return sellerMapper.insertSelective(seller);
}
第4章 商家审核
4.1 需求分析
商家申请入驻后,需要网站运营人员在运营商后台进行审核,审核后商家才可以登陆系统。
状态值: 0:未审核 1:已审核 2:审核未通过 3:关闭
4.2 商家待审核列表
4.2.1 修改seller_1.html
引入JS
<script src="/plugins/angularjs/angular.min.js"></script>
<!--分页相关引入-->
<link rel="stylesheet" href="/plugins/angularjs/pagination.css">
<script src="/plugins/angularjs/pagination.js"></script>
<!--引用-->
<script src="/js/base_pagination.js"></script>
<script src="/js/service/sellerService.js"></script>
<script src="/js/controller/baseController.js"></script>
<script src="/js/controller/sellerController.js"></script>
指令
<body ng-app="pinyougou" ng-controller="sellerController" ng-init="searchEntity={status:'0'}" class="hold-transition skin-red sidebar-mini" >
加入分页控件
<!--分页-->
<tm-pagination conf="paginationConf"></tm-pagination>
搜索条件
<div class="has-feedback">
公司名称:<input ng-model="searchEntity.name" >
店铺名称: <input ng-model="searchEntity.nickName">
<button class="btn btn-default" ng-click="getPage(1,10)" >查询</button>
</div>
循环
<tr ng-repeat="pojo in list">
<td><input type="checkbox"></td>
<td>{{pojo.sellerId}}</td>
<td>{{pojo.name}}</td>
<td>{{pojo.nickName}}</td>
<td>{{pojo.linkmanName}}</td>
<td>{{pojo.telephone}}</td>
<td class="text-center">
<button type="button" class="btn bg-olive btn-xs" data-toggle="modal" data-target="#sellerModal" >详情</button>
</td>
</tr>
4.3 商家详情
4.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.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.bankUser}}</td>
</tr>
<tr>
<td>开户行支行</td>
<td>{{entity.bankName}}</td>
</tr>
<tr>
<td>银行账号</td>
<td>999000111222</td>
</tr>
</table>
</div>
</div>
<!-- 选项卡结束 -->
列表的“详情”按钮
<button ng-click="getById(pojo.sellerId)" type="button" class="btn bg-olive btn-xs" data-toggle="modal" data-target="#sellerModal" >详情</button>
4.3.2 后台代码修改
由于代码生成器这里生成的根据ID查询是Long类型,而当前表Id是字符串,所以需要把入参改成String。
@RequestMapping(value = "/{id}",method = RequestMethod.GET)
public Seller getById(@PathVariable(value = "id")String id){
//根据ID查询Seller信息
Seller seller = sellerService.getOneById(id);
return seller;
}
4.4 商家审核
4.4.1 后端代码
在pinyougou-sellergoods-interface工程的SellerService.java服务接口新增方法定义
/**
* 审核状态修改
* @param id
* @param status
* @return
*/
int updateStatus(String id, String status);
在pinyougou-sellergoods-service的SellerServiceImpl.java新增方法
@Override
public int updateStatus(String id, String status) {
Seller seller = new Seller();
seller.setSellerId(id);
seller.setStatus(status);
//修改状态
return sellerMapper.updateByPrimaryKeySelective(seller);
}
在pinyougou-manager-web的SellerController.java新增方法
/***
* 商家状态审核
* @param id
* @param status
* @return
*/
@RequestMapping(value = "/status/{id}/{status}")
public Result updateStatus(@PathVariable(value = "id")String id,@PathVariable(value = "status")String status){
try {
//审核操作
int mcount = sellerService.updateStatus(id,status);
if(mcount>0){
return new Result(true,"审核成功");
}
} catch (Exception e) {
e.printStackTrace();
}
return new Result(false,"审核失败");
}
4.4.2 前端代码
修改pinyougou-manager-web的sellerService.js
//修改状态
this.updateStatusInfo=function (sellerid,status) {
return $http.post("/seller/status/"+sellerid+"/"+status+".shtml");
}
修改pinyougou-manager-web的sellerController.js
//修改状态
$scope.updateStatus=function(sellerid,status){
sellerService.updateStatusInfo(sellerid,status).success(function (response) {
//判断状态
if(response.success){
$scope.reloadList();
}else{
alert(response.message);
}
});
}
修改按钮,调用方法
<div class="modal-footer">
<button ng-click="updateStatus(entity.sellerId,'1')" class="btn btn-success" data-dismiss="modal" aria-hidden="true">审核通过</button>
<button ng-click="updateStatus(entity.sellerId,'2')" class="btn btn-danger" data-dismiss="modal" aria-hidden="true">审核未通过</button>
<button ng-click="updateStatus(entity.sellerId,'3')" class="btn btn-danger" data-dismiss="modal" aria-hidden="true">关闭商家</button>
<button class="btn btn-default" data-dismiss="modal" aria-hidden="true">关闭</button>
</div>
第5章 商家系统登录与安全控制
5.1 需求分析
完成商家系统登陆与安全控制,商家账号来自数据库,并实现密码加密
5.2 自定义认证类
在pinyougou-shop-web创建com.pinyougou.service包,包下创建类UserDetailsServiceImpl.java 实现UserDetailsService接口
@Component
public class UserDetailsServiceImpl implements UserDetailsService {
@Reference
private SellerService sellerService;
/***
* 自定义授权
* @param username
* @return
* @throws UsernameNotFoundException
*/
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//根据商户名查询商户信息
Seller seller = sellerService.getOneById(username);
if(seller!=null && seller.getStatus().equals("1")){
//加载商户权限,这里模拟商户权限
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
authorities.add(new SimpleGrantedAuthority("ROLE_SELLER"));
return new User(username,seller.getPassword(),authorities);
}
return null;
}
}
5.3 改造SpringSecurityConfig.java配置类
@Autowired
private UserDetailsService userDetailsService;
/****
* 用户授权
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//使用自定义的认证类实现授权
auth.userDetailsService(userDetailsService);
}
5.4 前端页面
修改shoplogin.html登录表单
<form action="/login" method="post" class="sui-form" 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>
<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()">登 录</a>
</div>
</form>
6.5 密码加密
6.5.1 BCrypt加密算法
用户表的密码通常使用MD5等不可逆算法加密后存储,为防止彩虹表破解更会先使用一个特定的字符串(如域名)加密,然后再使用一个随机的salt(盐值)加密。 特定字符串是程序代码中固定的,salt是每个密码单独随机,一般给用户表加一个字段单独存储,比较麻烦。 BCrypt算法将salt随机并混入最终加密后的密码,验证时也无需单独提供之前的salt,从而无需单独处理salt问题。
6.5.2 商家入驻密码加密
商家申请入驻的密码要使用BCrypt算法进行加密存储,修改SellerController.java的add方法
@RequestMapping(value = "/add",method = RequestMethod.POST)
public Result add(@RequestBody Seller seller){
try {
//密码加密,这里更推荐将该对象的实例交给spring容器管理
BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
seller.setPassword(bCryptPasswordEncoder.encode(seller.getPassword()));
//执行增加
int acount = sellerService.add(seller);
if(acount>0){
//增加成功
return new Result(true,"增加成功");
}
} catch (Exception e) {
e.printStackTrace();
}
return new Result(false,"增加失败");
}
这时候注册的密码就是密文了。
6.5.3 加密配置
修改pinyougou-shop-web的spring.xml ,添加如下配置
<!--加密对象-->
<beans:bean id="bcryptEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" />
修改SpringSecurityConfig.java的认证管理器的配置
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
/****
* 用户授权
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//使用自定义的认证类实现授权
auth.userDetailsService(userDetailsService)
.passwordEncoder(bCryptPasswordEncoder); //指定加密对象
}
6.5.4 SpringSecurityConfig.java完整代码
@Component
@EnableWebSecurity
public class SecurityHandler extends WebSecurityConfigurerAdapter {
/*****
* 忽略一些公开链接的权限设置
*/
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/seller/add.shtml");
web.ignoring().antMatchers("/*.html");
web.ignoring().antMatchers("/css/**");
web.ignoring().antMatchers("/img/**");
web.ignoring().antMatchers("/js/**");
web.ignoring().antMatchers("/plugins/**");
}
/****
* 其他非公开链接的权限设置以及其他访问设置
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
//其他链接地址都需要SELLER角色
http.authorizeRequests().antMatchers("/**").access("hasRole('SELLER')");
//登录设置
http.formLogin().loginPage("/shoplogin.html") //登录跳转地址
.loginProcessingUrl("/login") //登录处理地址
//.defaultSuccessUrl("/admin/index.html",true) //登录后始终跳转到后台首页
.successHandler(new AuthenticationSuccessHandler() {
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
//响应数据封装
Result result = new Result(true, "/admin/index.html");
writerResult(response, result);
}
})
//.failureForwardUrl("/shoplogin.html"); //登录失败后跳转地址
.failureHandler(new AuthenticationFailureHandler() {
//授权失败处理对象
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {
//响应数据封装
Result result = new Result(false, "账号或者密码不正确!");
//将相应数据转成JSON
writerResult(response, result);
}
});
//登出设置
http.logout().invalidateHttpSession(true) //让session无效
.logoutUrl("/logout") //登出处理地址
.logoutSuccessUrl("/shoplogin.html"); //登出后跳转地址
//发生异常跳转地址
http.exceptionHandling().accessDeniedPage("/error.html");
//允许跳转iframe
http.headers().frameOptions().disable();
//关闭csrf
http.csrf().disable();
}
/***
* 输出响应结果
* @param response
* @param result
* @throws IOException
*/
public void writerResult(HttpServletResponse response, Result result) throws IOException {
//将相应数据转成JSON
String jsonResult = JSON.toJSONString(result);
//设置相应编码
response.setContentType("application/json;charset=utf-8");
//获得输出对象
PrintWriter writer = response.getWriter();
writer.write(jsonResult);
writer.flush();
writer.close();
}
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
/****
* 用户授权
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//使用自定义的认证类实现授权
auth.userDetailsService(userDetailsService)
.passwordEncoder(bCryptPasswordEncoder); //指定加密对象
}
}
第6章 作业
6.1 显示登录名
参照运营商后台
6.2 退出登录
参照运营商后台
重点:
1、SpringSecurity入门案例
2、自定义登录
3、自定义认证类
4、加密登录
评论区