品优购项目记录:day04

今日目标:

        (1)实现 Spring Security 入门 Demo

        (2)完成运营商登录与安全控制功能

        (3)完成商家入驻

        (4)完成商家审核

        (5)完成商家系统登录与安全控制功能





1、运营商系统登录与安全控制


1.1 导入 Spring Security 依赖

        <!-- 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>

1.2 配置文件相关

(1)web.xml 新增配置
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring/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>

(2)新增spring-security.xml 文件
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
	xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
						http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd">

	<!-- 以下页面不被拦截 -->
	<http pattern="/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>

	<!-- 页面拦截规则 -->
	<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>

	<!-- 认证管理器 -->
	<authentication-manager>
		<authentication-provider>
			<user-service>
				<user name="admin" password="123456" authorities="ROLE_ADMIN"/>
				<user name="user" password="123456" authorities="ROLE_ADMIN"/>
			</user-service>
		</authentication-provider>
	</authentication-manager>


</beans:beans>


(3)指定登录页面,访问的action路径为Spring Security提供的/login,并配置账号密码提交的字段为username和password


 注意:提交路径,和name属性的值都是可以在配置文件中修改的,都可以在form-login 的属性中配置

<!--  
	login-processing-url="/sysLogin" : 配置登录请求的路径
	username-parameter="user" : 配置账号提交到的字段
	password-parameter="pwd" : 配置密码提交到的字段
-->

(4)指定表单id,并给登录按钮设置绑定事件,提交表单

 注意:

    (1)表单提交必须为post

    (2)提交路径、账号和密码字段,均可以自定义

    (3)登录成功默认是跳转到本次会话的上一次没有访问成功的页面,如果没有就跳转到默认登录成功页面,always-user-default-target="true"配置,可以设置,登陆成功总是跳转到默认登录成功页面,一般后台管理系统会配置。前台页面不配置,用户体验会更好。



1.3 登录后显示登录用户名

(1)后端代码,新建一个LoginController,用于获取登录名并返回到前端
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 java.util.HashMap;
import java.util.Map;

/**
 * 登录相关控制层
 * Author xushuai
 * Description
 */
@RestController
@RequestMapping("/login")
public class LoginController {


    /**
     * 返回当前登录用户名
     *
     * @return java.util.Map
     */
    @RequestMapping("/showName")
    public Map showName() {
        // 使用spring security的方法获取
        String name = SecurityContextHolder.getContext().getAuthentication().getName();
        // 封装到 Map 中
        Map<String, String> map = new HashMap<>();
        map.put("loginName", name);

        return map;
    }
}


(2)前端

    a、编写loginService.js

app.service('loginService', function ($http) {

    //获取登录用户名
    this.showName = function () {
        return $http.get('../login/showName.do');
    }
});

    b、编写indexService.js

app.controller('indexController', function ($scope, loginService) {

    // 显示当前登录用户名
    $scope.showName = function () {
        loginService.showName().success(
            function (rtn) {
                $scope.loginName = rtn.loginName;
            }
        );
    }
});

    c、页面引入js文件



    d、修改所有 "测试用户" 为 "{{loginName}}" ,使用查找替换



效果:




1.4 退出登录,只需要在 spring-security中的http节点中,配置 logout ,然后在前端页面中的注销按钮,请求该 /logout 即可

(1)配置


(2)注销按钮




2、商家申请入驻

2.1 前端

(1)为所有的输入框绑定提交变量


(2)给申请入驻按钮绑定单击事件


(3)修改前端新增 JS 代码

2.2 后端,只需要在保存之前,补全数据即可(sellergoods-service)



3、商家审核


3.1 待审核商家列表

(1)引入js,在页面添加分页控件,在body中引入 ng-app 和 ng-controller



(2)循环显示列表



(3)初始化的时候,设置搜索status=0



3.2 查看商家详情

(1)为详情按钮添加单击事件


(2)绑定变量到需要回显数据的地方


3.3 商家状态修改

(1)服务层接口(sellergoods-interface),新增方法
	/**
	 * 修改商家状态
	 * 
	 * @param sellerId 商家id
	 * @param status 状态
	 */
	void updateStatus(String sellerId, String status);

(2)服务层实现(sellergoods-service),实现
	@Override
	public void updateStatus(String sellerId, String status) {
		//查询商家
		TbSeller seller = sellerMapper.selectByPrimaryKey(sellerId);
		if(seller != null) {
			//修改状态
			seller.setStatus(status);
			//保存
			sellerMapper.updateByPrimaryKey(seller);
		}
	}

(3)控制层(SellerController)
    /**
     * 修改商家状态
     *
     * @return entity.Result
     */
    public Result updateStatus(String sellerId, String status) {
        try {
            sellerService.updateStatus(sellerId, status);
            return Result.success("修改成功");
        } catch (Exception e) {
            e.printStackTrace();
            return Result.error("修改失败");
        }
    }


(4)前端sellerService.js新增方法
	//更改状态
	this.updateStatus = function (sellerId, status) {
		return $http.get('../seller/updateStatus.do?sellerId=' + sellerId + '&status=' + status);
    }

(5)前端sellerController.js新增方法
	//修改商家状态
	$scope.updateStatus = function (sellerId, status) {
		sellerService.updateStatus(sellerId,status).success(
			function (rtn) {
				alert(rtn.message);
				if(rtn.success) {
                    $scope.reloadList();//刷新列表
				}
            }
		);
    }

(6)前端按钮添加单击事件




4、商家系统登录和安全控制


4.1 准备工作

(1)引入Spring Security依赖
(2)修改web.xml
(3)修改登录表单。提交路径为"/login";账号和密码提交的字段分别为 username 和password;给按钮添加单击事件,用于提交登录表单数据


4.2 商家登录

(1)编写自定义认证类,需要实现 UserDetailsService
package com.pinyougou.shop.security;

import com.pinyougou.pojo.TbSeller;
import com.pinyougou.sellergoods.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;

/**
 * Spring Security 自定义认证类
 * Author xushuai
 * Description
 */
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> authorities = new ArrayList<>();
        // 这个角色名必须在 Spring Security 配置文件中配置
        authorities.add(new SimpleGrantedAuthority("ROLE_SELLER"));

        //按用户名获取商家
        TbSeller seller = sellerService.findOne(username);
        if (seller != null) {
            // 判断商家状态是否合法
            if(seller.getStatus().equals(TbSeller.STATUS_CHECK)) {// 合法
                /*
                 * 进行校验:
                 *      Spring Security会自动校验输入的username、password,与User对象中的useranme和password进行校验
                 *      如果校验成功,就将角色列表中的角色赋予给当前登录的用户
                 */
                return new User(username, seller.getPassword(), authorities);
            }
        }

        return null;
    }
}

(2) spring-security.xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
	xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	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>

	<!-- 页面拦截规则 -->
	<http use-expressions="false">
		<intercept-url pattern="/**" access="ROLE_SELLER" />
		<!--
			login-processing-url="/sysLogin" : 配置登录请求的路径
			 username-parameter="user" : 配置账号提交到的字段
			 password-parameter="pwd" : 配置密码提交到的字段

			 always-use-default-target :
			 	总是跳转到默认的登录成功后显示的页面,如果不写这个配置,
			 	默认登录成功后首先跳转到当前会话上次没有访问成功的页面

		 -->
		<form-login login-page="/shoplogin.html"  default-target-url="/admin/index.html"
					authentication-failure-url="/shoplogin.html" always-use-default-target="true"/>
		<!-- 退出登录 -->
		<logout />
		<csrf disabled="true"/>
		<!-- 配置ifream允许访问 -->
		<headers>
			<frame-options policy="SAMEORIGIN"/>
		</headers>
	</http>

	<!-- 认证管理器 -->
	<authentication-manager>
		<!-- 指定自定认证类为认证提供者 -->
		<authentication-provider user-service-ref="userDetailsService"/>
	</authentication-manager>

	<!-- 配置自定义认证类 -->
	<beans:bean id="userDetailsService" class="com.pinyougou.shop.security.UserDetailsServiceImpl">
		<beans:property name="sellerService" ref="sellerService"/>
	</beans:bean>

	<!-- 引用dubbo 服务 -->
	<dubbo:application name="pinyougou-shop-web" />
	<dubbo:registry address="zookeeper://192.168.25.170:2181"/>
	<dubbo:reference id="sellerService" interface="com.pinyougou.sellergoods.service.SellerService"/>
</beans:beans>

4.3 BCrypt加密算法

        用户表的密码通常使用MD5等不可逆算法加密后存储,为防止彩虹表破解更会先使用一个特定的字符串(如域名)加密,然后再使用一个随机的salt(盐值)加密。 特定字符串是程序代码中固定的,salt是每个密码单独随机,一般给用户表加一个字段单独存储,比较麻烦。 BCrypt算法将salt随机并混入最终加密后的密码,验证时也无需单独提供之前的salt,从而无需单独处理salt问题。


4.4 商家入驻时,进行密码加密

(1)修改SellerController的add方法(shop-web)

(2)在spring-security.xml配置文件中,配置登录时的密码加密方式


4.5 商家管理与商家审核一致,参考商家审核



5、商家修改资料


5.1 回显数据到修改资料页面

(1)后端,LoginController(shop-web),新增方法获取当前登录用户的id
    /**
     * 返回当前登录用户ID
     */
    @RequestMapping("/sellerId")
    public String sellerId() {
        // 使用spring security的方法获取
        String name = SecurityContextHolder.getContext().getAuthentication().getName();

        return name;
    }

5.2 前端

(1)引入js文件,设置ng-app和ng-controller

(2)输入框绑定变量,回显数据




(3)loginService.js新增方法
    this.sellerId = function () {
        return $http.get('../login/sellerId.do');
    }

(4)sellerService.js新增方法
	// 使用id加载当前商家信息
	$scope.sellerId = "";
	$scope.loadId = function () {
		loginService.sellerId().success(
			function (rtn) {
                sellerId = JSON.parse(rtn);
                $scope.findOne(sellerId);
            }
		);
    }

注意:需要注入loginService服务,且前端页面要引入loginService.js文件


(5)页面初始化运行 loadId()

(6)效果





5.2 点击保存,修改资料(后端部分已由代码生成器生成)

(1)前端,sellerController.js新增方法
    //更新
    $scope.update=function(){
        sellerService.update( $scope.entity  ).success(
            function(response){
                if(response.success){
                    alert(response.message);
                    $scope.loadId();
                }else{
                    alert(response.message);
                }
            }
        );
    }

(2)为页面中的 保存按钮绑定单击事件





6、商家修改密码


6.1 后端

(0)新增一个实体类,用于接受前端传过来的新旧密码
package entity;

/**
 * 修改密码时,存放旧密码和新密码的实体
 * Author xushuai
 * Description
 */
public class Password {

    private String oldPwd;
    private String newPwd;

    public String getOldPwd() {
        return oldPwd;
    }

    public void setOldPwd(String oldPwd) {
        this.oldPwd = oldPwd;
    }

    public String getNewPwd() {
        return newPwd;
    }

    public void setNewPwd(String newPwd) {
        this.newPwd = newPwd;
    }
}




(1)服务层接口(sellergoods-interface),新增方法
    /**
     * 修改密码
     *
     * @param sellerId 商家id
     * @param oldPwd   旧密码
     * @param newPwd   新密码
     */
    void updatePassword(String sellerId, String newPwd);

(2)服务层实现(sellergoods-service),实现
	@Override
	public void updatePassword(String sellerId, String newPwd) {
		// 查询商家
		TbSeller seller = sellerMapper.selectByPrimaryKey(sellerId);
		if(seller != null) {
			// 修改密码
			seller.setPassword(newPwd);
			sellerMapper.updateByPrimaryKey(seller);
		}

	}

(3)控制层,shop-web下的SellerController(重点是使用 BCrypt.checkpw() 进行密码校验)
    @RequestMapping("/updatePassword")
    public Result updatePassword(@RequestBody Password password) {
        try {
            // 对密码进行加密处理
            BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
            String newPwd = passwordEncoder.encode(password.getNewPwd());
            //获取当前登录的用户id
            String name = SecurityContextHolder.getContext().getAuthentication().getName();
            TbSeller seller = findOne(name);
            //校验两个密码是否一致
            if(BCrypt.checkpw(password.getOldPwd(),seller.getPassword())) {//一致
                sellerService.updatePassword(name, newPwd);
                return Result.success("修改密码成功");
            }

            return Result.error("原密码错误");
        } catch (Exception e) {
            e.printStackTrace();
            return Result.error("修改密码失败");
        }
    }


6.2 前端

(1)引入js相关



(2)绑定变量到输入框

(3) sellerController.js新增方法
    // 修改密码
	$scope.updatePassword = function () {
        //校验两次密码是否一致
		if($scope.newPwd != $scope.newPwd1) {
			alert("两次密码输入不一致!");
		} else {
			$scope.password={oldPwd:$scope.oldPwd,newPwd:$scope.newPwd};
			sellerService.updatePassword($scope.password).success(
				function (rtn) {
					alert(rtn.message);
                }
			);
		}
    }


(4)sellerService.js新增方法
	//修改密码
	this.updatePassword = function (password) {
		return $http.post('../seller/updatePassword.do', password);
    }

(5)保存按钮绑定单击事件



猜你喜欢

转载自blog.csdn.net/qq1031893936/article/details/80957905