[转]Discuz!论坛通行证与JSP网站的整合

1.需求
需求:本项目网站需要和Discuz论坛实现同步登录、注销与注册。
2.引言.
Discuz论坛提供的Passport(通行证)接口可以很好的实现上述需求。通行证可以在Discuz论坛的系统设置中开启,开启通行证之后的论坛将不再接受除管理员以外的用户的登录请求,而与应用网站进行统一登录管理。整合之后的用户注册、登录、注销流程,请阅读参考资料[1],强烈建议看本文档之前先看一下参考资料[1],熟悉一下整合的流程。本文档的大部分代码是参照上面所说的文章,在其基础上修改而来。下面介绍实现的整个流程,并给出完整代码。
3.流程
应用程序与论坛整合的大体流程是:1、开发加密、解密类,以及密钥类 2、修改应用程序的登录、注销、注册的页面,使其能够满足同步登录的需要 3、在Discuz论坛的系统设置中启用通行证,并设置应用网站的地址及私钥(当然,在调试阶段,通行证应该一直是处于启用状态的)。注:步骤2与3是没有先后顺序的,即一旦开始进行步骤2,无论是先做的登录、注销还是注册,都应该开启Discuz论坛的通行证,以方便测试,否则即使代码正确也不会实现同步。
4.涉及的页面(以下URL为举例)
Discuz! 的 URL 为http://127.0.0.1:8088/index.php
项目程序的 URL 为http://localhost:8081/
项目程序的注册页面为user/userregpre.do
项目程序的登录页面为index.do
项目程序的退出页面为user/userlogout.do
以上这些会在下面的代码中和discuz论坛的通行证的配置中用到。
5.实现过程
5.1 开发DiscuzPassport及Encryption两个类,实现与Discuz一致的数据加密。这两个类是由上文说的成熟案例得到。经测试这两个类方法完全符合要求并且无错误,所以直接套用即可。

-----DiscuzPassport.java-----

import java.io.UnsupportedEncodingException;
import java.util.Map;
import java.util.Iterator;
import java.util.Random;
import java.util.Set;
import sun.misc.BASE64Decoder;



public class DiscuzPassport {

public static String encrypt(String src, String key) {
Random random = new Random();
random.setSeed(System.currentTimeMillis());
String rand = "" + random.nextInt() % 32000;
String encKey = Encryption.generateKey(rand, "MD5");
int ctr = 0;
String tmp = "";
for (int i = 0; i < src.length(); i++) {
ctr = (ctr == encKey.length() ? 0 : ctr);
tmp += encKey.charAt(ctr);
char c = (char) (src.charAt(i) ^ encKey.charAt(ctr));
tmp += c;
ctr++;
}
String passportKey = passportKey(tmp, key);
return new sun.misc.BASE64Encoder().encode(passportKey.getBytes());
}

public static String decrypt(String src, String key) {
byte[] bytes = null;
try {
bytes = new BASE64Decoder().decodeBuffer(src);
src = new String(bytes);
} catch (Exception e) {
return null;
}
src = passportKey(src, key);
String tmp = "";
for (int i = 0; i < src.length(); ++i) {
char c = (char) (src.charAt(i) ^ src.charAt(++i));
tmp += c;
}
return tmp;
}

public static String passportKey(String src, String key) {
String encKey = Encryption.generateKey(key, "MD5");
int ctr = 0;
String tmp = "";
for (int i = 0; i < src.length(); ++i) {
ctr = (ctr == encKey.length() ? 0 : ctr);
char c = (char) (src.charAt(i) ^ encKey.charAt(ctr));
tmp += c;
ctr++;
}
return tmp;
}

public static String passportEncode(Map data) {
Set keys = data.keySet();
String key = "";
String ret = "";
Iterator iterator = keys.iterator();
while (iterator.hasNext()) {
key = (String) iterator.next();
try
{
ret += key + "=" + (String) data.get(key) + "&";
}
catch (Exception e)
{
return "";
}
}
if (ret.length() > 0)
return ret.substring(0, ret.length() - 1);
return "";
}
}

------------------------------


-----Encryption.java-----

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;



public class Encryption {

public static String generateKey(String src, String algorithm) {
MessageDigest m = null;
try
{
m = MessageDigest.getInstance(algorithm);
m.update(src.getBytes("UTF8"));
}
catch (NoSuchAlgorithmException e)
{
e.printStackTrace();
}
catch (UnsupportedEncodingException e)
{
e.printStackTrace();
}
byte s[] = m.digest();
String result = "";
for (int i = 0; i < s.length; i++)
{
result += Integer.toHexString(
(0x000000FF & s[i]) | 0xFFFFFF00).substring(6);
}
return result;
}
}

------------------------------

5.2 修改几个页面,实现网站之间的整合,涉及的页面包括(注:看本部分时请参考原项目代码,效果会好些):

index.php(Discuz论坛首页,包含登录、注册与退出的链接)
ACT_UserLogin.java(项目网站的登录业务处理页面,这里将进行修改)
ACT_UserLogout.java(项目网站的注销业务处理页面,这里将进行修改)
ACT_UserReg.java(项目网站的注销业务处理页面,这里将进行修改)

5.2.1登录

从论坛登录,页面跳转过程为:
index.php -> index.do ->userlogin.do ->index.php

从项目网站登录,页面跳转过程为:
index.do ->userlogin.do ->index.php ->index.do

用户在论坛首页点击登录后,index.php将首先指向index.do,并以GET方式向其传递一个forward变量,用来存储登录后跳转的目标。这里,forward一般都是论坛首页,即http://127.0.0.1:8088/index.php。index.do主要负责给出用户登录界面,并以POST方式向forward变量。
logined.jsp负责处理从项目网站和论坛两处提交过来的登录申请。因此,它需要首先判断上一个页面是否提交了forward变量,如果没有,那么申请就是从应用网站提交过来的,只需按正常的流程登录(设置自身的cookie或session);如果有,那么除了按正常流程登录外,还需要对用户的登录信息进行加密,并按规定的格式传递给index.php(这部分工作由上面两个类来完成)。
而从项目网站登录的时候,userlogin.do由于没有接收到从index.do传过来的forward,所以会自行生成forward,这个时候forward存的是项目程序的登录URL,即http://localhost:8081/index.do,这样在Action转向discuz以后,又会从新跳转回项目网站。
userlogin.do中调用两个类的关键代码如下(用红色标注部分,从注明“开始”到“结束”之间):

-----userlogin.do(部分)-----



public class ACT_UserLogin extends BaseAction {

public ACT_UserLogin() {
super();
}

public ActionForward doExcute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response,
ActionMessages errors) throws Exception {
//获取数据
String strLoginName =request.getParameter("loginname");
String strLoginPass =request.getParameter("loginpass");
String strReturnUrl =request.getParameter("returnurl");

if(strLoginName ==null || strLoginPass ==null){
throw new Exception("error.common.badrequest");
}

//验证数据
this.doValidate(errors, INFO_User.validateLoginName(strLoginName));
this.doValidate(errors, INFO_User.validateLoginPass(strLoginPass));
if(!errors.isEmpty()){
return null;
}

//以下是关于应用程序与论坛的同步登录
HttpSession session =request.getSession();
String forward=null;
//下面是判断forward是否为空,为空则说明请求是有项目本身传递的,则自行为//froward赋值
if(request.getSession().getAttribute("forward")!=null){
forward=(String)session.getAttribute("forward");
}else{
forward="http://localhost:8081/index.do";
}

//为三个变量赋值
String username=strLoginName;
String password=strLoginPass;
//传递到论坛的数据中,电子邮件地址是必须传递的部分,如果项目中没有的话则先写//个固定的即可
String email="lyp_roc@163.com";

Map mb = new LinkedHashMap();
mb.put("time", "" + System.currentTimeMillis());
mb.put("username", username);
mb.put("password", password);
mb.put("email", email);
//注意这里的私钥一定要和discuz中的私钥相同,否则便会解密信息失败

//下面的auth,verify,forward所存的具体值与具体意义,请参照文档[1].
String key = "liyupeng01"; //私钥
String enc=DiscuzPassport.passportEncode(mb);
String auth = DiscuzPassport.encrypt(enc, key);

String verify = "login" + auth + forward + key;
verify = Encryption.generateKey(verify, "MD5");

//location就是要跳转的URL
String location = "http://127.0.0.1:8088/api/passport.php?action=login&auth=" + java.net.URLEncoder.encode(auth, "UTF-8") + "&forward=" +java.net.URLEncoder.encode(forward, "UTF-8") + "&verify=" + verify;
//结束
//处理请求
BO_User boUser =new BO_User();
INFO_User infoUser =boUser.userLogin(strLoginName, strLoginPass);
//设置到Session
session.setAttribute("loginuser", infoUser);
//设置资源下载列表
ArrayList downFileList =new ArrayList();
session.setAttribute("downfilelist", downFileList);

//进行跳转
if(strReturnUrl !=null &&!strReturnUrl.equals("null")){
//这里即为跳转部分
return new ActionForward(location, true);
}else{
return new ActionForward("/index.do", true);
}

}
}

------------------------------

5.2.2注销

从论坛注销,页面跳转过程为:
index.php -> userlogout.do -> index.php
从项目网站注销,页面跳转过程为:
userlogout.do -> index.php -> index.do


-----logout.jsp(部分)-----

public class ACT_UserLogout extends BaseAction {

public ACT_UserLogout() {
super();
}

public ActionForward doExcute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response,
ActionMessages errors) throws Exception {
//获取返回地址
//String strReturnUrl =request.getParameter("returnurl");
HttpSession session =request.getSession();
session.removeAttribute("loginuser");
session.removeAttribute("downfilelist");

//以下是论坛与应用程序同步注销功能代码

//判断注销命令是从discuz传递的还是从项目程序传递的
//如果来自项目本身,则Action自行给forward赋值
String forward=null;
if(request.getParameter("forward") != null){
forward=request.getParameter("forward");
System.out.println(forward+"discuz!");
}else{
forward="http://localhost:8081/index.do";
}
//密钥
String key = "liyupeng01";
String verify = "logout" + forward + key;
verify = Encryption.generateKey(verify, "MD5");

String location = "http://127.0.0.1:8088/api/passport.php?action=logout&" + "&forward=" + java.net.URLEncoder.encode(forward, "UTF-8") + "&verify=" + verify;
System.out.println(location);

//跳转
// if(strReturnUrl !=null && !strReturnUrl.equals("null")){
//完成跳转
return new ActionForward(location, true);
//结束
// }else{
// return new ActionForward("/index.do", true);
// }
}

}

------------------------------

5.2.3注册
由于要求论坛在后台管理中关闭论坛的注册功能,所以只有项目程序有注册的功能,在这里将把forward写死。此部分的代码其实和登录部分的代码几乎一样,读者参照登录部分代码就可以比较出来。
页面跳转过程为:
ACT_UserReg.java -> index.php ->ACT_UserReg.java
-----userreg.do(部分)-----
public class ACT_UserReg extends BaseAction {

public ACT_UserReg() {
super();
}

public ActionForward doExcute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response,
ActionMessages errors) throws Exception {
//验证session中的activecard
HttpSession session =request.getSession();
INFO_Card infoCard =(INFO_Card) session.getAttribute("activecard");
INFO_Cardtype infoCardtype =(INFO_Cardtype)session.getAttribute("activecardtype");
INFO_User infoAgentUser =(INFO_User)session.getAttribute("agentuser");
if(infoCard ==null){
throw new Exception("error.action.user.act_userreg.error1");
}

//获取用户数据
Integer iSex =null;
Long lAgentUserId =new Long(0);
String strLoginName =request.getParameter("loginname");
String strLoginPass =request.getParameter("loginpass");
String strRealName =request.getParameter("realname");
String strSex =request.getParameter("sex");

//验证数据
this.doValidate(errors, INFO_User.validateLoginName(strLoginName));
this.doValidate(errors, INFO_User.validateLoginPass(strLoginPass));
this.doValidate(errors, INFO_User.validateRealName(strRealName));
if(!errors.isEmpty()){
return null;
}

//以下是关于应用程序与论坛的同步注册
//直接写死forward
String forward=forward="http://localhost:8081/index.do";

//为三个变量赋值
String username=strLoginName;
String password=strLoginPass;
//测试用
String email="lyp_roc@163.com";

Map mb = new LinkedHashMap();
mb.put("time", "" + System.currentTimeMillis());
mb.put("username", username);
mb.put("password", password);
mb.put("email", email);


String key = "liyupeng01"; //私钥
String enc=DiscuzPassport.passportEncode(mb);
String auth = DiscuzPassport.encrypt(enc, key);



String verify = "login" + auth + forward + key;
verify = Encryption.generateKey(verify, "MD5");
//最终跳转的URL
String location = "http://127.0.0.1:8088/api/passport.php?action=login&auth=" + java.net.URLEncoder.encode(auth, "UTF-8") + "&forward=" +
java.net.URLEncoder.encode(forward, "UTF-8") + "&verify=" + verify;
//整合部分结束
//转换数据
iSex =new Integer(strSex);
if(infoAgentUser !=null){
lAgentUserId =infoAgentUser.getId();
}
//处理请求
BO_User boUser =new BO_User();
boUser.userReg(strLoginName, strLoginPass, strRealName, iSex, infoCardtype.getUserType(), infoCard.getId(), lAgentUserId);
//从session中移除activecard和activecardtype
session.removeAttribute("activecard");
session.removeAttribute("activecardtype");
session.removeAttribute("agentuser");
//跳转
return new ActionForward(location, true);
}

}

------------------------------


5.3 在Discuz论坛的系统设置中启用通行证,并设置应用网站的地址及私钥。(当然,在调试阶段,通行证应该一直是处于启用状态的)
用超级管理员进入论坛后台管理界面以后(用户名和密码都是admin),选择 “扩展设置” ->”通行证 API”,即可进入通行证设置。需要配置的选项有:
应用程序 URL 地址:http://localhost:8081/
通行证私有密匙: liyupeng01
应用程序注册地址: user/userregpre.do
应用程序登录地址: index.do
应用程序退出地址: user/userlogout.do
如果不是很明白,就参照参考文档[1]的要求配置,上面的说明很详细。(参考下图)

至此,Discuz!论坛通行证与JSP网站的整合工作全部完成了。

6.注意事项
. 1、项目程序与论坛的密钥一定要完全相同。

2、在启动论坛通行证功能之前,千万要把论坛登录的URL,与超级管理员退出的URL备份,因为调试失败时需要关闭通行证功能从新来,如果没有备份这两个的话就不进入不了论坛了。想关闭的时候用论坛登录URL进入,而不是直接点击登录进入,因为这样会进入项目程序的登录界面。而用admin开启论坛通行证之后,如果想注销admin,则只能用admin的URL来注销了。URL例子:
discuz登录:http://127.0.0.1:8088/logging.php?action=login
admin退出:http://127.0.0.1:8088/logging.php?action=logout&formhash=828f5a79

3、如果没有做注册,就做了登录的整合,那么可以在项目程序和论坛中同时申请一个用户名相同的用户,这样如果代码正确的话就可以实现同步登录。

4、如果测试的时候,失败以后想注销论坛中已经登录的用户却注销不了(因为通行证已经开启,注销功能已经与项目程序同步,点击注销只会跳转到项目程序中的注销界面),那么可以到论坛的数据库中,直接删除该用户(admin用户千万别这么做),重启Apache即可。

5、注意在代码套用的时候一定要记得把所有东西全部改成自己的,不然找都不好找。

6注意,千万不能在应用程序中也申请一个和论坛的超级管理员用户名与密码都相同的用户(admin),这样会造成论坛的超级管理员进入不了后台管理界面,这个时候只能回复以前的数据库中的members表了。

7、以后修改数据库尤其是涉及到admin管理员用户之前,千万要备份数据库!!!
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在信号处理领域,DOA(Direction of Arrival)估计是一项关键技术,主要用于确定多个信号源到达接收阵列的方向。本文将详细探讨三种ESPRIT(Estimation of Signal Parameters via Rotational Invariance Techniques)算法在DOA估计中的实现,以及它们在MATLAB环境中的具体应用。 ESPRIT算法是由Paul Kailath等人于1986年提出的,其核心思想是利用阵列数据的旋不变性来估计信号源的角度。这种算法相比传统的 MUSIC(Multiple Signal Classification)算法具有较低的计算复杂度,且无需进行特征值分解,因此在实际应用中颇具优势。 1. 普通ESPRIT算法 普通ESPRIT算法分为两个主要步骤:构造等效旋不变系统和估计角度。通过空间平移(如延时)构建两个子阵列,使得它们之间的关系具有旋不变性。然后,通过对子阵列数据进行最小二乘拟合,可以得到信号源的角频率估计,进一步换为DOA估计。 2. 常规ESPRIT算法实现 在描述中提到的`common_esprit_method1.m`和`common_esprit_method2.m`是两种不同的普通ESPRIT算法实现。它们可能在实现细节上略有差异,比如选择子阵列的方式、参数估计的策略等。MATLAB代码通常会包含预处理步骤(如数据归一化)、子阵列构造、旋不变性矩阵的建立、最小二乘估计等部分。通过运行这两个文件,可以比较它们在估计精度和计算效率上的异同。 3. TLS_ESPRIT算法 TLS(Total Least Squares)ESPRIT是对普通ESPRIT的优化,它考虑了数据噪声的影响,提高了估计的稳健性。在TLS_ESPRIT算法中,不假设数据噪声是高斯白噪声,而是采用总最小二乘准则来拟合数据。这使得算法在噪声环境下表现更优。`TLS_esprit.m`文件应该包含了TLS_ESPRIT算法的完整实现,包括TLS估计的步骤和旋不变性矩阵的改进处理。 在实际应用中,选择合适的ESPRIT变体取决于系统条件,例如噪声水平、信号质量以及计算资源。通过MATLAB实现,研究者和工程师可以方便地比较不同算法的效果,并根据需要进行调整和优化。同时,这些代码也为教学和学习DOA估计提供了一个直观的平台,有助于深入理解ESPRIT算法的工作原理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值