提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
前言
提示:这里可以添加本文要记录的大概内容:
有这样一个需求,我们需要设计一个用户认证类,使用策略模式实现多种认证算法(例如用户名密码认证、短信验证码认证、第三方登录认证),并可以选择使用不同的认证算法来进行用户认证。
我们使用springboot+vue来完成登录功能和前端页面的展示,并使用策略加工厂模式来实现对不同认证算法的实现。后续考虑到密码的安全性问题,我们会使用装饰模式在不修改源代码的情况下,新增一个密码md5+salt盐并且反转的加密方法,并将当前登陆的用打印到txt文档中。
(需要源代码的可以私信联系我喔)
提示:以下是本篇文章正文内容,下面案例可供参考
一、后端代码
类图和项目结构
策略加工厂模式代码
策略接口
package com.demo.strategyService;
import com.demo.common.LoginServiceEnum;
import com.demo.domain.RequestLogin;
public interface LoginService {
LoginServiceEnum getStrategy();
Boolean doLogin(RequestLogin user);
}
实现
package com.demo.strategyService;
import com.demo.common.LoginServiceEnum;
import com.demo.domain.RequestLogin;
import org.springframework.stereotype.Component;
@Component
public class EmailLoginStrategy implements LoginService{
@Override
public LoginServiceEnum getStrategy() {
return LoginServiceEnum.LOGIN_EMAIL;
}
@Override
public Boolean doLogin(RequestLogin user) {
System.out.println("这是短信登录");
return true;
}
}
package com.demo.strategyService;
import com.demo.common.LoginServiceEnum;
import com.demo.domain.DemoUser;
import com.demo.domain.RequestLogin;
import com.demo.mapper.DemoUserMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
@Component
@Slf4j
public class NormalLoginStrategy implements LoginService{
@Resource
private DemoUserMapper demoUserMapper;
@Override
public LoginServiceEnum getStrategy() {
return LoginServiceEnum.LOGIN_NOR;
}
@Override
public Boolean doLogin(RequestLogin user) {
DemoUser userByName = demoUserMapper.getUserByName(user.getUsername());
if (user.getUsername() != "" && user.getUsername().equals(userByName.getUsername())){
if (userByName.getPassword() != null && user.getPassword().equals(userByName.getPassword())){
log.info("这是普通的登录方法,登录成功");
return true;
}
return false;
}
return false;
}
}
package com.demo.strategyService;
import com.demo.common.LoginServiceEnum;
import com.demo.domain.RequestLogin;
import org.springframework.stereotype.Component;
@Component
public class WeChatLoginStrategy implements LoginService{
@Override
public LoginServiceEnum getStrategy() {
return LoginServiceEnum.LOGIN_WECHAT;
}
@Override
public Boolean doLogin(RequestLogin user) {
System.out.println("这是WEChAT登录方法");
return true;
}
}
工厂类
package com.demo.factory;
import com.demo.common.LoginServiceEnum;
import com.demo.strategyService.LoginService;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Component
public class LoginTypeFactory implements InitializingBean {
@Resource
List<LoginService> loginServiceList;
private Map<LoginServiceEnum,LoginService> handlerMap = new HashMap<>();
public LoginService getStrategy(int subjectType){
// ResultCodeEnum resultCodeEnum = SubjectInfoTypeEnum.getByCode(subjectType);
LoginServiceEnum resultCodeEnum = LoginServiceEnum.getByCode(subjectType);
System.out.println(resultCodeEnum);
return handlerMap.get(resultCodeEnum);
}
@Override
public void afterPropertiesSet() throws Exception {
//在Spring容器初始化这个类(LoginTypeFactory)的时候,
// 会自动查找Spring容器中所有的LoginFactory类型的Bean,并将这些Bean注入到loginFactories这个列表中。
// 这样,在SubjectTypeHandlerFactory类中就可以直接使用这个列表,
// 而不需要手动去查找和获取这些SubjectTypeHandler类型的Bean。
for (LoginService loginService : loginServiceList){
handlerMap.put(loginService.getStrategy(),loginService);
}
}
}
枚举类
package com.demo.common;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Objects;
@Getter
@AllArgsConstructor
public enum LoginServiceEnum {
LOGIN_NOR(1, "普通登录"),
LOGIN_EMAIL(2, "短信登录"),
LOGIN_WECHAT(3,"微信登录");
private Integer code;
private String desc;
LoginServiceEnum(int code, String desc){
this.code = code;
this.desc = desc;
}
public static LoginServiceEnum getByCode(int codeVal){
for(LoginServiceEnum resultCodeEnum : LoginServiceEnum.values()){
if(resultCodeEnum.code == codeVal){
return resultCodeEnum;
}
}
return null;
}
}
返回结果集
package com.demo.common;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class MYResult<T> {
//状态码
private Integer code ;
//消息提示
private String message ;
//数据 List , Map, String , Book , Student
private T data ;
//操作成功,没有数据返回
public static<T> MYResult<T> success(){
return new MYResult<>(20000, "操作成功" ,null);
}
//操作成功,返回数据
public static<T> MYResult<T> success(T data){
return new MYResult<>(20000, "操作成功" ,data);
}
public static<T> MYResult<T> success(String message){
return new MYResult<>(20000, message ,null);
}
public static<T> MYResult<T> success(String message, T data){
return new MYResult<>(20000, message ,data);
}
public static<T> MYResult<T> success(Integer code , String message, T data){
return new MYResult<>(code, message ,data);
}
public static<T> MYResult<T> fail(){
return new MYResult<>(60204, "操作失败" ,null);
}
public static<T> MYResult<T> fail(String message){
return new MYResult<>(60204, message ,null);
}
public static<T> MYResult<T> fail(Integer code , String message){
return new MYResult<>(code , message , null);
}
public static<T> MYResult<T> fail(Integer code , String message, T data){
return new MYResult<>(code , message , data);
}
}
实体类
package com.demo.domain;
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 java.io.Serializable;
/**
*
* @TableName demo_user
*/
@TableName(value ="demo_user")
public class DemoUser implements Serializable {
/**
*
*/
@TableId(type = IdType.AUTO)
private Integer id;
/**
*
*/
private String username;
/**
*
*/
private String password;
@TableField(exist = false)
private static final long serialVersionUID = 1L;
/**
*
*/
public Integer getId() {
return id;
}
/**
*
*/
public void setId(Integer id) {
this.id = id;
}
/**
*
*/
public String getUsername() {
return username;
}
/**
*
*/
public void setUsername(String username) {
this.username = username;
}
/**
*
*/
public String getPassword() {
return password;
}
/**
*
*/
public void setPassword(String password) {
this.password = password;
}
@Override
public boolean equals(Object that) {
if (this == that) {
return true;
}
if (that == null) {
return false;
}
if (getClass() != that.getClass()) {
return false;
}
DemoUser other = (DemoUser) that;
return (this.getId() == null ? other.getId() == null : this.getId().equals(other.getId()))
&& (this.getUsername() == null ? other.getUsername() == null : this.getUsername().equals(other.getUsername()))
&& (this.getPassword() == null ? other.getPassword() == null : this.getPassword().equals(other.getPassword()));
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((getId() == null) ? 0 : getId().hashCode());
result = prime * result + ((getUsername() == null) ? 0 : getUsername().hashCode());
result = prime * result + ((getPassword() == null) ? 0 : getPassword().hashCode());
return result;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(getClass().getSimpleName());
sb.append(" [");
sb.append("Hash = ").append(hashCode());
sb.append(", id=").append(id);
sb.append(", username=").append(username);
sb.append(", password=").append(password);
sb.append(", serialVersionUID=").append(serialVersionUID);
sb.append("]");
return sb.toString();
}
}
package com.demo.domain;
import lombok.Data;
@Data
public class RequestLogin {
private String username;
private String password;
private Integer type;
}
mapper层代码及其xml
package com.demo.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.demo.domain.DemoUser;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
/**
* @author 18512
* @description 针对表【demo_user】的数据库操作Mapper
* @createDate 2024-04-10 20:22:03
* @Entity com.status.demo.domain.DemoUser
*/
@Mapper
public interface DemoUserMapper extends BaseMapper<DemoUser> {
DemoUser getUserByName(String Name);
Integer regsiterUser(@Param("username") String username, @Param("password") String password);
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.demo.mapper.DemoUserMapper">
<resultMap id="BaseResultMap" type="com.demo.domain.DemoUser">
<id property="id" column="id" jdbcType="INTEGER"/>
<result property="username" column="username" jdbcType="VARCHAR"/>
<result property="password" column="password" jdbcType="VARCHAR"/>
</resultMap>
<sql id="Base_Column_List">
id,username,password
</sql>
<insert id="regsiterUser" >
insert into demo_user (username, password)
values (#{username}, #{password});
</insert>
<select id="getUserByName" resultType="com.demo.domain.DemoUser">
select *
from demo_user;
</select>
</mapper>
service层代码及其实现
package com.demo.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.demo.domain.DemoUser;
/**
* @author 18512
* @description 针对表【demo_user】的数据库操作Service
* @createDate 2024-04-10 20:22:03
*/
public interface DemoUserService extends IService<DemoUser> {
Integer registeUser(String username, String password);
}
package com.demo.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.demo.mapper.DemoUserMapper;
import com.demo.service.DemoUserService;
import org.springframework.stereotype.Service;
import com.demo.domain.DemoUser;
/**
* @author 18512
* @description 针对表【demo_user】的数据库操作Service实现
* @createDate 2024-04-10 20:22:03
*/
@Service
public class DemoUserServiceImpl extends ServiceImpl<DemoUserMapper, DemoUser>
implements DemoUserService {
@Override
public Integer registeUser(String username, String password) {
return this.baseMapper.regsiterUser(username,password);
}
}
yml文件及其pom.xml文件
server:
port: 9093
spring:
application:
name: MyDemo01
datasource:
druid:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/strategydemo?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true
username: root
password: 1234
mybatis-plus:
configuration:
#在映射实体或者属性时,将数据库中表名和字段名中的下划线去掉,按照驼峰命名法映射
map-underscore-to-camel-case: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
banner: false
mapper-locations: classpath:mapper/*.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>MyAuthRoleChain</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>StrategyDemo</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.26</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>19.0</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>19.0</version>
</dependency>
</dependencies>
</project>
二、前端代码
1。前端项目结构
登录页面及其HomeCompent’页面
<template>
<div>
<h2>Login</h2>
<form @submit.prevent="login">
<div>
<label for="username">Username:</label>
<input v-model="credentials.username" type="text" id="username" required>
</div>
<div>
<label for="password">Password:</label>
<input v-model="credentials.password" type="password" id="password" required>
</div>
<div>
<label for="type">Login Type:</label>
<select v-model="credentials.type" id="type">
<option value="1">普通登录</option>
<option value="2">短信登录</option>
<option value="3">微信登录</option>
</select>
</div>
<button type="submit">Login</button>
</form>
</div>
</template>
<script>
import axios from 'axios';
export default {
name: 'LoginComponent',
data() {
return {
credentials: {
username: '',
password: '',
type: '1', // 默认为 "普通登录"
},
};
},
methods: {
async login() {
try {
const response = await axios.post('http://localhost:9093/login', this.credentials);
if (response.data.code === 20000) {
alert(`登录成功: ${response.data.message}`);
// 使用 this.$router 访问路由实例,并跳转到 HomeComponent
this.$router.push({ name: 'home' });
} else {
alert(`登录失败: ${response.data.message}`);
}
} catch (error) {
console.error('登录错误:', error);
alert('登录失败: 发生错误');
}
},
},
};
</script>
<template>
<div>
<h1>Welcome to Home Page!</h1>
<p>This is a simple home page component.</p>
</div>
</template>
<script>
export default {
name: 'HomeComponent',
// Options API 里可以定义 data, methods, computed 等选项
data() {
return {
// 定义你的响应式数据
};
},
methods: {
// 定义方法
},
// 其他选项如 computed, watch, mounted 等生命周期钩子也可以在这里定义
};
</script>
<style scoped>
/* 你的CSS样式 */
</style>
router路由
import { createRouter, createWebHistory } from 'vue-router'
import HomeComponent from '../components/HomeComponent.vue'
import LoginComponent from '../components/LoginComponent.vue'
const routes = [
{ path: '/', component: LoginComponent },
{ path: '/home', component: HomeComponent, name: 'home' }
// 添加其他路由配置
]
const router = createRouter({
history: createWebHistory(),
routes
})
export default router
App.vue文件
效果
注册