实战项目:电商管理系统(SpringBoot)Java后端 -【未完】

注意:
这篇文章我打算先搁亿搁,
我先完成:Spring Security OAuth2.0 认证协议
然后再开启这篇文章,并且打算用 springsecurity 把 shiro 和cas 给换掉

初衷

为前端做支持
(所以比较简单)

在这里插入图片描述

项目概述

根据不同的应用场景,电商系统一般都提供了 PC 端、移动 APP、 移动 Web 、微信小程序等多种终端访问方式。

在这里插入图片描述

# 功能、页面原型

在这里插入图片描述

环境搭建

# 后台项目初始化(SpringBoot)

## 导入 MySQL

Github - 下载整个 - 后端项目

or

单独下载 SQL 脚本

# 登录
mysql -uroot -proot  –-default-character-set=utf8
# 创建 vsdb 数据库(vue-shop database) utf8编码
create database vsdb charset utf8 ; 
# 进入数据库
use vsdb ;
# 导入数据 数据在github的v-shop-server\db目录下
source F:\environment\java\workspace\v-shop-server\db\vsdb.sql ; 

在这里插入图片描述

## API 文档下载地址

Github - 下载整个 - 后端项目

or

单独下载 api文档

## 创建 SpringBoot 项目

在这里插入图片描述

在这里插入图片描述

添加 Mybatis-Plus 依赖

		<dependency><!-- Mybatis-plus --> <!-- MP核心库 2.0.8版本以上,CRUD不支持二级缓存 -->
		    <groupId>com.baomidou</groupId>
		    <artifactId>mybatis-plus-boot-starter</artifactId>
		    <version>3.2.0</version>
		</dependency>

## 项目启动测试

application.yml

server:
  port: 3001
  servlet:
    context-path: /api/private/v1

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/vsdb?useUnicode=true&characterEncoding=utf-8
    username: root
    password: root

修改 host

# 数据库
192.168.64.33 vdb.cn 

HelloController.java

package cn.shop.controller;

import java.util.Date;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RequestMapping("/hello")
@RestController
public class HelloController {

	@RequestMapping("/now")
	public Date showTime() {
		return new Date() ; 
	}
}

在这里插入图片描述
http://localhost:3001/api/private/v1/hello/now
在这里插入图片描述

## Github 托管

在这里插入图片描述

Github - 下载整个 - 后端项目

# 准备: JsonResult + CORS 跨域支持

引入三个工具类:CookieUtil、JsonUtil、JsonResult

根据下面状态码,进行相应修改(挺多修改的哦,建议下载来对一下,太多,就不贴上来)

在这里插入图片描述

创建测试接口

package cn.shop.controller;

@RequestMapping("/hello")
@RestController
public class HelloController {

	@RequestMapping("/cors")
	public JsonResult corsJsonResult() {
		return JsonResult.ok("cors success!");
	}
}

实现 CORS 跨域支持

创建 CorsConfiger 类

package cn.shop.common.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

@Configuration
public class CorsConfiger {

	private CorsConfiguration corsConfig() {
		CorsConfiguration corsConfiguration = new CorsConfiguration();
		/*
		 * 请求常用的三种配置,*代表允许所有,当然你也可以自定义属性(比如header只能带什么,只能是post方式等等)
		 */
		// 允许访问的客户端域名
		corsConfiguration.addAllowedOrigin("*");
		// 允许任何请求头 可选: "x-requested-with,X-Nideshop-Token,X-URL-PATH"
		corsConfiguration.addAllowedHeader("*");
		// 允许任何方法 可选: "POST, GET, OPTIONS, DELETE"
		corsConfiguration.addAllowedMethod("*");
		// 允许请求带有验证信息
		corsConfiguration.setAllowCredentials(true);
		// maxAge(3600)表明在3600秒内,不需要再发送预检验请求,可以缓存该结果
		corsConfiguration.setMaxAge(3600L);
		return corsConfiguration;
	}

	@Bean
	public CorsFilter corsFilter() {
		UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
		// 拦截本域名下的所有请求
		source.registerCorsConfiguration("/**", corsConfig()); // 设置头配置信息
		return new CorsFilter(source);
	}
}

测试

localhost:8080

var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://localhost:3001/api/private/v1/hello/cors');
xhr.send(null);
xhr.onload = function(e) {
    var xhr = e.target;
    console.log(xhr.responseText);
}

在这里插入图片描述
done!

提供数据库

host

192.168.64.33 vdb.cn

端口 3306

# manager 类(分别创建 mapper、service、controller)

对应数据库中的,manager(系统管理员)

加密策略

(shiro、simplehash 加密)

加密算法 : MD5
加密次数:3
密文长度:32

账号 密码 密文 颜值
admin admin 4e06b3cee0dc656695020bf34e07220f d5a76533-c05d-49e5-8653-6bc1397dc6ce
linken javacas 68a3f5b307f4ca10750993ffdc3bf1a6 b83b2044-00b5-4525-92a2-89ce8eb2fb79
asdf1 asdf1 116e8a4f2553aa7c23b866c23f93b975 7e3c19c2-5a29-4a0f-8956-60f55bc24b58
asdf123 asdf123 b81c3a9a920620d2e9e2718e963947bc 40fb1593-4b85-4c1e-88b1-5375c172167b

根据数据库信息,创建 manager 类

package cn.shop.model.po;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;

import lombok.Data;
import lombok.experimental.Accessors;

import javax.validation.constraints.NotEmpty;

@Accessors(chain = true)
@Data
@TableName("sp_manager")
public class Manager {

    /**
     * 主键 id
     */
    @TableId(type = IdType.AUTO, value = "mg_id")
    private Integer id;
    /**
     * 名称
     */
    @NotEmpty(message = "用户名不能为空")
    @TableField(value = "mg_name")
    private String name;
    /**
     * 密码
     */
    @NotEmpty(message = "密码不能为空")
    @TableField(value = "mg_pwd")
    private String password;
    /**
     * 盐值
     */
    @NotEmpty(message = "盐值异常")
    @TableField(value = "mg_salt")
    private String salt;
    /**
     * 注册时间
     */
    @TableField(value = "mg_time")
    private Long createTime;
    /**
     * 角色 id
     */
    @TableField(value = "role_id")
    private Integer roleId;
    @TableField(value = "mg_mobile")
    private String mobile;
    @TableField(value = "mg_email")
    private String email;
    /**
     * 0:启用 1:过期
     */
    @TableField("mg_expired")
    private Integer expired;
    /**
     * 0:启用 1:禁用
     */
    @TableField("mg_disabled")
    private Integer disabled;
}


ManagerMapper

@Mapper
public interface ManagerMapper extends BaseMapper<Manager>{

}

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://vdb.cn:3306/vsdb?useUnicode=true&characterEncoding=utf-8
    username: root
    password: root

搭建 CAS 服务端 (可用静态TOKEN代替)

cas 服务端 v-cas https://cas.cn:8443/cas

.
修改 host

# Cas Server
127.0.0.1 cas.cn
# 后期改为 192.168.64.10

端口 8443

弄这个目的,是为了获取真实的 token 和 实现 SSO
不想弄的话,可以用静态TOKEN代替
.
.
下图为,整个 架构图 和 官方整理的 cas 工作流程图
.
架构图
在这里插入图片描述
cas 工作流程图
在这里插入图片描述

搭建 cas 客户端 (集成 shiro + cas)

添加 host

127.0.0.1      vshop.cn

端口: 3001

https://blog.csdn.net/catoop/article/details/50534006
https://www.jianshu.com/p/3c6680daab0e

添加依赖

  • shiro-cas 是 shiro 自 1.2 版本后添加的对 CAS 的官方实现
  • cas-client-core 是 CAS 的核心包
<!--Apache Shiro所需的jar包 -->
<dependency>
	<groupId>org.apache.shiro</groupId>
	<artifactId>shiro-spring</artifactId>
	<version>1.2.4</version>
</dependency>
<dependency>
	<groupId>org.apache.shiro</groupId>
	<artifactId>shiro-cas</artifactId>
	<version>1.2.4</version>
</dependency>
<dependency>
	<groupId>org.jasig.cas.client</groupId>
	<artifactId>cas-client-core</artifactId>
	<version>3.2.1</version>
</dependency>

编写 ShiroCasRealm

  • 现在项目要通过 CAS 实现 SSO ,说明用户名密码的验证已经在 CAS Server 实现,服务端验证通过后返回到项目的是一个验证通过的唯一标识
  • 所以编写一个 ShiroCasRealm ,继承自 CasRealm(CasRealm 继承自 AuthorizingRealm) ,来完成对 CAS Server 返回数据的验证
  • 现在只写认证,授权 doGetAuthorizationInfo() 暂时不写。
package cn.shop.common.config.shiro;

import cn.shop.model.po.Manager;
import cn.shop.service.ManagerService;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.cas.CasAuthenticationException;
import org.apache.shiro.cas.CasRealm;
import org.apache.shiro.cas.CasToken;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.util.CollectionUtils;
import org.jasig.cas.client.authentication.AttributePrincipal;
import org.jasig.cas.client.validation.Assertion;
import org.jasig.cas.client.validation.TicketValidator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.util.List;

/**
 * @author lawsssscat
 */
public class ShiroCasRealm extends CasRealm {

    @Autowired
    private ManagerService managerService;

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        // 授权,暂时不写
        return super.doGetAuthorizationInfo(principals);
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        CasToken casToken = (CasToken) token;

        // token 为空直接返回,页面会重定向到 Cas Server 页面,并且携带本项目回调页
        // 如: https://cas.cn:8443/cas/login?service=http://vshop.cn/manager
        if (ObjectUtils.isEmpty(token)) {
            return null;
        }

        // 获取服务端返回的票根
        String ticket = (String) casToken.getCredentials();

        // 票根为空直接返回,页面会重定向到 Cas Server 页面,并且携带本项目回调页
        // 如: https://cas.cn:8443/cas/login?service=http://vshop.cn/manager
        if (StringUtils.isEmpty(ticket)) {
            return null;
        }

        // 票根验证器
        TicketValidator ticketValidator = ensureTicketValidator();

        try {
            // 票根验证,会将 ticket 再次发送给 Cas Server ,Cas Server 验证 ticket 的有效性并返回判断 Assertion (返回包含用户数据)
            Assertion casAssertion = ticketValidator.validate(ticket, getCasService());
            // 获取服务端返回的用户数据
            AttributePrincipal casPrincipal = casAssertion.getPrincipal();

            // 拿到用户唯一标识(这个标识是 Cas Server 传过来的,我们规定为 id 信息)
            String userId = casPrincipal.getName();
            Integer id = Integer.parseInt(userId);

            // 通过唯一标识查询数据库用户表
            // 如果查询到对应用户,则直接返回用户数据
            // 如果没有查询到用户数据,则向数据库新增用户并返回用户数据
            Manager manager = managerService.findById(id);

            // 将获取到的本项目数据库用户包装为 shiro 自身的 principal 存于当前 session 中
            // 之后再整个项目中都可以通过 SecurityUtils.getSubject().getPrincipal() 直接去到当前用户信息
            List<Object> principals = CollectionUtils.asList(manager, casPrincipal.getAttributes());
            SimplePrincipalCollection principalCollection = new SimplePrincipalCollection(principals, getName());

            return new SimpleAuthenticationInfo(principalCollection, ticket);
        } catch (Exception e) {
            throw new CasAuthenticationException("Unable to validate ticket {" + ticket + "}", e);
        }
    }
}

然后是 配置 ShiroManager ,让 shrio 动起来
编写 Shiro 配置类 (必须!)

  • 在配置类中,调用自定义 Realm

  • 创建 casSubjectFactory 是默认的工厂类
    (这里,喜欢可以加 shiroCacheManager 缓存管理器,参考 https://www.jianshu.com/p/3c6680daab0e

  • 创建 securityManager 默认的安全管理器

    并且,将 realm、subjectFactory 配置进 manager

最后,过滤器

  • 登出过滤器
    logoutFilter 是 shiro 官方实现的 CAS 登出规则过滤器,只需要调用并填写重定向的回调地址即可

    redirectUrl 表示用户在本项目中执行登出操作后,会重定向到 CAS Server 的登出页,同时携带再次登录成功后的本项目登录页

  • 通用资源过滤器
    filters 中分别指定了 logoutFilter 和 casFilter 映射的别名,会在后续请求映射规则中中使用


首次登陆

浏览器shiro服务器cas服务器GET 数据浏览器登录了吗?没有没 tokenPOST 登录信息POST 登录信息首次登录全局session存储:token=aaatoken=aaatoken=aaaGET 数据(token=aaa)验证 token =aaatoken 没问题局部session存储:token=aaa数据。。。。。。GET 数据数据浏览器shiro服务器cas服务器

已经登录

浏览器shiro服务器cas服务器GET 数据浏览器登录了吗?已经登录token=aaa登录了,token=aaa局部session存储:token=aaa数据,token=aaa。。。。。。GET 数据数据浏览器shiro服务器cas服务器

# 可能的问题

  • Bad Request (如下图)
    在这里插入图片描述
    原因:cas 默认 https 访问的。
    (TLS是https的一种加密算法)
    解决:去 cas 服务器开启 http 访问

    修改 cas Server 中的:

    • application.properties:server.ssl.enabled=false
    • vshop.json"serviceId" : "^(https|imaps|http)://vshop.*",

    或者:shiro 作为 cas 客户端,弄个 cas 服务器中认证的证书

登录/退出功能

# shiro

shiro 官方教程 - http://shiro.apache.org/spring.html
我的 shrio 笔记【⭐️⭐️⭐️】 - https://blog.csdn.net/LawssssCat/article/details/104027123

添加shiro依赖

<dependency>
   <groupId>org.apache.shiro</groupId>
   <artifactId>shiro-spring</artifactId>
   <version>1.4.1</version>
</dependency>

在这里插入图片描述

主页布局

用户管理模块

权限管理模块

分类管理模块

参数管理模块

商品管理模块

订单管理模块

数据统计模块

发布了520 篇原创文章 · 获赞 190 · 访问量 3万+
App 阅读领勋章
微信扫码 下载APP
阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 撸撸猫 设计师: 设计师小姐姐

分享到微信朋友圈

×

扫一扫,手机浏览