品优购第四天

时间:2022-07-23
本文章向大家介绍品优购第四天,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

品优购 第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()" >登&nbsp;&nbsp;录</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">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</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()">登&nbsp;&nbsp;录</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、加密登录