单点登录(十七)----cas4.2.x登录mongodb验证方式成功后返回更多信息更多属性到客户端
我们在之前已经完成了cas4.2.x登录使用mongodb验证方式登录成功了。也解决了登录名中使用中文乱码的问题。
单点登录(十三)-----实战-----cas4.2.X登录启用mongodb验证方式完整流程
单点登录(十五)-----实战-----cas4.2.x登录mongodb验证方式实现自定义加密
单点登录(十六)-----遇到问题-----cas4.2.x登录成功后报错No principal was found---cas中文乱码问题完美解决
AttributePrincipal principal = (AttributePrincipal) request.getUserPrincipal();
String username = principal.getName();
默认情况下cas server只返回用户名username到客户端。
但是 一般情况我们的程序中也可能遇到需要得到更多如姓名,手机号,email,用户权限等更多用户信息的情况。
现在我们需要cas4.2.x登录mongodb验证方式成功后返回更多信息更多属性到客户端。
步骤如下:
确认数据库字段
我们现在的用户集合如下,现在需要把用户名和phone以及crmRoles返回过来。
{
"_id": ObjectId('5563e73eb0905d938b1c4799'),
"username": "crm",
"password": "05ff5d58703d197b007320ebffeb22e98f8fc3103552277812053abe4c048897",
"roles": [
"*"
],
"phone": "13522222222",
"crmRoles": [
"manager"
]
}
cas.properties配置文件新增属性
我们在配置文件cas.properties中录入的属性来获取其他属性。
自定义接口
IPersonAttributeDao (org.jasig.services.persondir.IPersonAttributeDao) 接口,这个是用来定义我们需要返回给客户端相关信息的接口,CAS SERVER 默认有提供许多实现,比如
LdapPersonAttributeDao :通过查询 LDAP 目录 ,来返回信息
SingleRowJdbcPersonAttributeDao : 通过JDBC SQL查询,来返回信息
NamedStubPersonAttributeDao
StubPersonAttributeDao
等等,还有许多,大家可以参考源码中的实现,CAS SERVER 提供了各种功能的实现,有时候我们可以直接使用这个现成的就行了。
但是mongodb在cas server 4.2.x中是没有现成的接受属性转换接口的,所以我们需要自定义实现一个IPersonAttributeDao 。
想要实现IPersonAttributeDao 接口,我们需要对cas server的登录流程有所了解。
用户输入的帐号密码是组装成credential 来通过验证的。
通过验证后根据我们在配置文件中录入的属性来获取其他属性。
验证成功后会把用户帐号信息和属性组装成一个结果result,这个结果result是包含我们设置的属性的。
这里通过resolver把credential 和result的信息转化成 principal。
principal就是我们要传给客户端做交互的容器。
而怎么把credential 和result的信息转化成 principal,拿哪些信息则是由我们的attributeRepository属性定义的。
也就是
<bean id="attributeRepository" p:backingMap-ref="attrRepoBackingMap"
class="org.jasig.services.persondir.support.NamedStubPersonAttributeDao" />
所以这里用NamedStubPersonAttributeDao是不对的,我们需要新建自定义一个实现了IPersonAttributeDao 的接口,接收credential 和result里的属性信息填充到 principal。
我们这里新建一个MongoDBPersonAttributeDao.java类实现IPersonAttributeDao 接口,关键是要实现getPerson等方法。
我们可以参考SingleRowJdbcPersonAttributeDao类来写,SingleRowJdbcPersonAttributeDao 是对jdbc数据库进行属性提取的。对mongodb属性提取也是类似的。
在实现getPerson过程中getPerson只接受了用户名参数,所以我们要得到所有属性的话需要用mongodb去访问一次。(我们发现在personDirectoryPrincipalResolver层其实可以把result直接传进去再组装,这样就不需要再去访问一次mongdb了,这样需要新建一个类实现PrincipalResolver,然后替换掉personDirectoryPrincipalResolver,这种方式感兴趣的同学可以试试)
所以我们又新建了一个PersonMongoDB.java类,过程可以参考MongoAuthenticationHandler类。
在返回IPersonAttributes过程中我们发现还需要一个实现了IPersonAttributes的类,
所以我们又新建了一个MongoDBIPersonAttributes。
这三个文件新建在cas-server-support-mongo项目中。
MongoDBPersonAttributeDao.java
package self;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.jasig.services.persondir.IPersonAttributeDao;
import org.jasig.services.persondir.IPersonAttributes;
public class MongoDBPersonAttributeDao implements IPersonAttributeDao {
private IPersonAttributes backingPerson = null;
Map<String, List<Object>> backingMap=new HashMap<String, List<Object>>();
PersonMongoDB personMongoDB;
@Override
public IPersonAttributes getPerson(String uid) {
if (uid == null) {
throw new IllegalArgumentException("Illegal to invoke getPerson(String) with a null argument.");
}
this.backingPerson=this.personMongoDB.buildPersonMongoDB(uid,backingMap);
return this.backingPerson;
}
@Override
public Set<IPersonAttributes> getPeople(Map<String, Object> query) {
// TODO Auto-generated method stub
return null;
}
@Override
public Set<IPersonAttributes> getPeopleWithMultivaluedAttributes(
Map<String, List<Object>> query) {
// TODO Auto-generated method stub
return null;
}
@Override
public Set<String> getPossibleUserAttributeNames() {
// TODO Auto-generated method stub
return null;
}
@Override
public Set<String> getAvailableQueryAttributes() {
// TODO Auto-generated method stub
return null;
}
@Override
public Map<String, List<Object>> getMultivaluedUserAttributes(
Map<String, List<Object>> seed) {
// TODO Auto-generated method stub
return null;
}
@Override
public Map<String, List<Object>> getMultivaluedUserAttributes(String uid) {
// TODO Auto-generated method stub
return null;
}
@Override
public Map<String, Object> getUserAttributes(Map<String, Object> seed) {
// TODO Auto-generated method stub
return null;
}
@Override
public Map<String, Object> getUserAttributes(String uid) {
// TODO Auto-generated method stub
return null;
}
/**
* Set the Map which this stub object will return for all legal invocations of
* attributesForUser().
*
* @param backingMap The backingMap to set, may not be null.
*/
public void setBackingMap(final Map<String, List<Object>> backingMap) {
this.backingMap=backingMap;
}
public void setPersonMongoDB(PersonMongoDB personMongoDB) {
this.personMongoDB = personMongoDB;
}
}
PersonMongoDB.java
package self;
import static com.mongodb.client.model.Filters.eq;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.bson.Document;
import org.jasig.services.persondir.IPersonAttributes;
import org.pac4j.core.exception.AccountNotFoundException;
import org.pac4j.core.exception.MultipleAccountsFoundException;
import org.pac4j.mongo.profile.MongoProfile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Repository;
import com.mongodb.MongoClient;
import com.mongodb.MongoClientURI;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoCursor;
import com.mongodb.client.MongoDatabase;
@Repository("personMongoDB")
public class PersonMongoDB {
private static final Logger LOGGER = LoggerFactory
.getLogger(PersonMongoDB.class);
@Value("${cas.authn.mongo.collection.name:users}")
private String collectionName;
@Value("${cas.authn.mongo.db.name:cas}")
private String databaseName;
@Value("${cas.authn.mongo.db.host:}")
private String mongoHostUri;
@Value("${cas.authn.mongo.attributes:}")
private String attributes;
@Value("${cas.authn.mongo.username.attribute:username}")
private String usernameAttribute;
@Value("${cas.authn.mongo.password.attribute:password}")
private String passwordAttribute;
public PersonMongoDB() {
}
@SuppressWarnings("resource")
public IPersonAttributes buildPersonMongoDB(String uid,
Map<String, List<Object>> backingMap) {
final String username = uid;
final MongoClientURI uri = new MongoClientURI(this.mongoHostUri);
final MongoClient mongoClient = new MongoClient(uri);
LOGGER.info("Connected to MongoDb instance @ {} using database [{}]",
uri.getHosts(), uri.getDatabase());
final MongoDatabase db = mongoClient.getDatabase(uri.getDatabase());
final MongoCollection<Document> collection = db.getCollection(collectionName);
final List<Document> users = new ArrayList<>();
try (final MongoCursor<Document> cursor = collection.find(eq(usernameAttribute, username)).iterator()) {
int i= 0;
while (cursor.hasNext() && i <= 2) {
users.add(cursor.next());
i++;
}
}
if (users.isEmpty()) {
throw new AccountNotFoundException("No account found for: " + username);
} else if (users.size() > 1) {
throw new MultipleAccountsFoundException("Too many accounts found for: " + username);
} else {
Map<String, List<Object>> attributes=new HashMap<String, List<Object>>();
final Map<String, Object> user = users.get(0);
for (final Map.Entry<String, List<Object>> attrEntry : backingMap.entrySet()) {
final String keydb = attrEntry.getKey();
List<Object> value = attrEntry.getValue();
if (value != null) {
String keyClient=(String) value.get(0);
List<Object> obs=new ArrayList<Object>();
obs.add(user.get(keydb));
attributes.put(keyClient, obs);
}
}
IPersonAttributes person=new MongoDBIPersonAttributes(username,attributes);
return person;
}
}
protected MongoProfile createProfile(final String username,
final String[] attributes, final Map<String, Object> result) {
final MongoProfile profile = new MongoProfile();
profile.setId(username);
for (String attribute : attributes) {
profile.addAttribute(attribute, result.get(attribute));
}
return profile;
}
public void setMongoHostUri(final String mongoHostUri) {
this.mongoHostUri = mongoHostUri;
}
public void setCollectionName(final String collectionName) {
this.collectionName = collectionName;
}
public void setDatabaseName(final String databaseName) {
this.databaseName = databaseName;
}
public void setAttributes(final String attributes) {
this.attributes = attributes;
}
public void setUsernameAttribute(final String usernameAttribute) {
this.usernameAttribute = usernameAttribute;
}
public void setPasswordAttribute(final String passwordAttribute) {
this.passwordAttribute = passwordAttribute;
}
}
MongoDBIPersonAttributes.java
package self;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.jasig.services.persondir.IPersonAttributes;
public class MongoDBIPersonAttributes implements IPersonAttributes {
/**
*
*/
private static final long serialVersionUID = 1634751256066729431L;
private final Map<String, List<Object>> attributes;
private String userNameAttribute;
public MongoDBIPersonAttributes(String username, Map<String, List<Object>> attributes) {
final Map<String, List<Object>> immutableValuesBuilder = this.buildImmutableAttributeMap(attributes);
this.attributes = Collections.unmodifiableMap(immutableValuesBuilder);
this.userNameAttribute=username;
}
@Override
public String getName() {
final Object attributeValue = this.getAttributeValue(this.userNameAttribute);
if (attributeValue == null) {
return null;
}
return attributeValue.toString();
}
@Override
public Map<String, List<Object>> getAttributes() {
return this.attributes;
}
@Override
public Object getAttributeValue(String name) {
final List<Object> values = this.attributes.get(name);
if (values == null || values.size() == 0) {
return null;
}
return values.get(0);
}
@Override
public List<Object> getAttributeValues(String name) {
final List<Object> values = this.attributes.get(name);
if (values == null || values.size() == 0) {
return null;
}
return values;
}
/**
* Take the constructor argument and convert the Map and List values into read-only form.
*
* @param attributes Map of attributes
* @return Read-only map of attributes
*/
protected Map<String, List<Object>> buildImmutableAttributeMap(final Map<String, List<Object>> attributes) {
final Map<String, List<Object>> immutableValuesBuilder = this.createImmutableAttributeMap(attributes.size());
for (final Map.Entry<String, List<Object>> attrEntry : attributes.entrySet()) {
final String key = attrEntry.getKey();
List<Object> value = attrEntry.getValue();
if (value != null) {
value = Collections.unmodifiableList(value);
}
immutableValuesBuilder.put(key, value);
}
return immutableValuesBuilder;
}
/**
* Create the Map used to store the attributes internally for this IPersonAttributes
*
* @param size size of map
* @return Map to store attributes
*/
protected Map<String, List<Object>> createImmutableAttributeMap(final int size) {
return new LinkedHashMap<>(size > 0 ? size : 1);
}
}
cas-server配置属性attributeRepository
首先cas-server需要配置属性attributeRepository,配置属性attributeRepository是从数据库获取更多的用户信息。
你需要到cas-server-webapp的WEB-INF目录找到
deployerConfigContext.xml文件
原attributeRepository属性为:
<bean id="attributeRepository" p:backingMap-ref="attrRepoBackingMap" class="org.jasig.services.persondir.support.NamedStubPersonAttributeDao"/>
原attrRepoBackingMap属性为:
<util:map id="attrRepoBackingMap">
<entry value="uid" key="uid"/>
<entry value="eduPersonAffiliation" key="eduPersonAffiliation"/>
<entry value="groupMembership" key="groupMembership"/>
<entry>
<key>
<value>memberOf</value>
</key>
<list>
<value>faculty</value>
<value>staff</value>
<value>org</value>
</list>
</entry>
</util:map>
配置attributeRepository和attrRepoBackingMap如下:
<bean id="attributeRepository" p:backingMap-ref="attrRepoBackingMap" p:personMongoDB-ref="personMongoDB" class="self.MongoDBPersonAttributeDao" /> <bean id="personMongoDB" class="self.PersonMongoDB" /> <!-- 要获取的属性在这里配置 --> <util:map id="attrRepoBackingMap"> <entry value="name" key="username"/> <entry value="phone" key="phone"/> <entry value="roles" key="crmRoles"/> <!--value为提供给客户端获取的属性名字,key为对应的数据库字段名称,系统会自动填充值--> </util:map>
这里启用我们上面写的转换属性类self.MongoDBPersonAttributeDao。
在使用过程中需要把self.PersonMongoDB当作bean传进去,这样才能personMongoDB才能获取到@value中的mongodb配置参数值。
cas-server修改casServiceValidationSuccess.jsp文件
此文件是将用户登录成功后,将信息生成XML传递给客户端,原文件是只包含name信息,所以需要修改,新增获取其他属性的代码如下:
<!-- 在server验证成功后,这个页面负责生成与客户端交互的xml信息,在默认的casServiceValidationSuccess.jsp中,只包括用户名,并不提供其他的属性信息,因此需要对页面进行扩展 -->
<c:if test="${fn:length(assertion.chainedAuthentications[fn:length(assertion.chainedAuthentications)-1].principal.attributes) > 0}">
<cas:attributes>
<c:forEach var="attr" items="${assertion.chainedAuthentications[fn:length(assertion.chainedAuthentications)-1].principal.attributes}">
<cas:${fn:escapeXml(attr.key)}>${fn:escapeXml(attr.value)}</cas:${fn:escapeXml(attr.key)}>
</c:forEach>
</cas:attributes>
</c:if>
原casServiceValidationSuccess.jsp为:
<%@ page session="false" contentType="application/xml; charset=utf-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
<cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'>
<cas:authenticationSuccess>
<cas:user>${fn:escapeXml(principal.id)}</cas:user>
<c:if test="${not empty pgtIou}">
<cas:proxyGrantingTicket>${pgtIou}</cas:proxyGrantingTicket>
</c:if>
<c:if test="${fn:length(chainedAuthentications) > 0}">
<cas:proxies>
<c:forEach var="proxy" items="${chainedAuthentications}" varStatus="loopStatus" begin="0" end="${fn:length(chainedAuthentications)}" step="1">
<cas:proxy>${fn:escapeXml(proxy.principal.id)}</cas:proxy>
</c:forEach>
</cas:proxies>
</c:if>
</cas:authenticationSuccess>
</cas:serviceResponse>
新增后为:
<%@ page session="false" contentType="application/xml; charset=utf-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
<cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'>
<cas:authenticationSuccess>
<cas:user>${fn:escapeXml(principal.id)}</cas:user>
<c:if test="${not empty pgtIou}">
<cas:proxyGrantingTicket>${pgtIou}</cas:proxyGrantingTicket>
</c:if>
<c:if test="${fn:length(chainedAuthentications) > 0}">
<cas:proxies>
<c:forEach var="proxy" items="${chainedAuthentications}" varStatus="loopStatus" begin="0" end="${fn:length(chainedAuthentications)}" step="1">
<cas:proxy>${fn:escapeXml(proxy.principal.id)}</cas:proxy>
</c:forEach>
</cas:proxies>
</c:if>
<!-- 在server验证成功后,这个页面负责生成与客户端交互的xml信息,在默认的casServiceValidationSuccess.jsp中,只包括用户名,并不提供其他的属性信息,因此需要对页面进行扩展 -->
<c:if test="${fn:length(assertion.chainedAuthentications[fn:length(assertion.chainedAuthentications)-1].principal.attributes) > 0}">
<cas:attributes>
<c:forEach var="attr" items="${assertion.chainedAuthentications[fn:length(assertion.chainedAuthentications)-1].principal.attributes}">
<cas:${fn:escapeXml(attr.key)}>${fn:escapeXml(attr.value)}</cas:${fn:escapeXml(attr.key)}>
</c:forEach>
</cas:attributes>
</c:if>
</cas:authenticationSuccess>
</cas:serviceResponse>
客户端获取更多用户信息
修改完成后,客户端就可以获取更多用户信息了
我这里用的是springMVC框架
后端:
接受过来的数组会变成字符串新建一个类用来把处理它还原成List
CommonUtils.java
package com.test.util;
import java.util.ArrayList;
import java.util.List;
public class CommonUtils {
/**
* 数组字符串转换成ArrayList
*
* @param object
* @return
*/
public static List<String> arrayStringtoArrayList(String object) {
object = object.replaceAll("\"", "");
object = object.replaceAll("\\[", "");
object = object.replaceAll("\\]", "");
String[] a = object.split(",");
List<String> list = new ArrayList<String>();
for(int i=0; i<a.length; i++){
String b = a[i].trim();
list.add(b);
}
return list;
}
}
IndexController.java
/**
* IndexController
*
*
*/
@Controller
public class IndexController {
@RequestMapping("/")
public String index(Model model, HttpServletRequest request)
throws IOException {
AttributePrincipal principal = (AttributePrincipal) request
.getUserPrincipal();
String username = principal.getName();
model.addAttribute("username", username);
if (principal != null) {
Map<String, Object> attributes = principal.getAttributes();
if (attributes.size() > 0) {
String name = (String) attributes.get("name");
String phone = (String) attributes.get("phone");
List<String> roles =CommonUtils.arrayStringtoArrayList((String)attributes.get("roles"));
model.addAttribute("name", name);
model.addAttribute("phone", phone);
model.addAttribute("roles", roles);
}
}
return "/index";
}
}
index.jsp页面
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>My JSP 'index.jsp' starting page</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<!--
<link rel="stylesheet" type="text/css" href="styles.css">
-->
<script type="text/javascript" src="<%=basePath%>/res/plugin/jquery.min.js"></script></head>
<body >
我是index页面 <br>
用户名:${username}<br>
属性名:${name}<br>
电话:${phone}<br>
角色:${roles[0]}
</body>
</html>
效果
ps:快捷的实现方式
上面自定义接口确实复杂了些,如果不需要字段对应等功能的话,有一种很快的方式不需要自定义接口也可以实现所有属性返回到客户端。
因为在跟踪代码过程中发现当resolver为null时会自动取result中的principal,它是包含所有属性的。
所以我们只需要把deployerConfigContext.xml文件中的里的proxyPrincipalResolver去掉就行了
<util:map id="authenticationHandlersResolvers">
<entry key-ref="proxyAuthenticationHandler" value-ref="proxyPrincipalResolver" />
<entry key-ref="primaryAuthenticationHandler" value-ref="primaryPrincipalResolver" />
</util:map>
修改为:
<util:map id="authenticationHandlersResolvers">
<entry key-ref="primaryAuthenticationHandler" value-ref="primaryPrincipalResolver" />
</util:map>
单点登录(十七)----cas4.2.x登录mongodb验证方式成功后返回更多信息更多属性到客户端的更多相关文章
- 单点登录(十一)-----遇到问题-----cas启用mongodb验证方式报错--Unable to locate Spring NamespaceHandler for XML schema na
cas启用mongodb验证方式报错--Unable to locate Spring NamespaceHandler for XML schema namespace [http://www.sp ...
- 单点登录(十)-----遇到问题-----cas启用mongodb验证方式报错com.mongodb.CommandFailureException---Authentication failed
cas启用mongodb验证方式报错com.mongodb.CommandFailureException---Authentication failed. 完整报错信息: 二月 08, 2017 5 ...
- 单点登录(十五)-----实战-----cas4.2.x登录mongodb验证方式实现自定义加密
我们在前一篇文章中实现了cas4.2.x登录使用mongodb验证方式. 单点登录(十三)-----实战-----cas4.2.X登录启用mongodb验证方式完整流程 也学习参考了cas5.0.x版 ...
- 单点登录(十三)-----实战-----cas4.2.X登录启用mongodb验证方式完整流程
我们在之前的文章中中已经讲到了正确部署运行cas server 和 在cas client中配置. 在此基础上 我们去掉了https的验证,启用了http访问的模式. 单点登录(七)-----实战-- ...
- 单点登录(十四)-----实战-----cas5.0.x登录mongodb验证方式常规的四种加密的思考和分析
我们在上一篇文章中已经讲解了cas4.2.X登录启用mongodb验证方式 单点登录(十三)-----实战-----cas4.2.X登录启用mongodb验证方式完整流程 但是密码是明文存储的,也就是 ...
- 单点登录(十二)-----遇到问题-----cas启用mongodb验证方式登录后没反应-pac4j-mongo包中的MongoAuthenticatInvocationTargetException
cas启用mongodb验证方式登录后没反应 控制台输出 2017-02-09 20:27:15,766 INFO [org.jasig.cas.authentication.MongoAuthent ...
- CAS3.5.2 Server登录后返回用户信息详细解决方案
单点登录(Single Sign-On, 简称SSO)是目前比较流行的服务于企业业务整合的解决方案之一,SSO使得在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统.大家在使用时CA ...
- httpclient 怎么带上登录成功后返回的cookie值访问下一页面
我是只很菜很菜的小鸟.刚上班,有这个一个需求.要我抓取别的网站的数据. 我根据用户密码登录一个网站成功后,生成一个cookie值.我已经获取到了.然后要带上这个cookie值进行下一页面的访问 ...
- httpclient 登录成功后返回的cookie值访问下一页面
HttpClient4.x可以自带维持会话功能,只要使用同一个HttpClient且未关闭连接,则可以使用相同会话来访问其他要求登录验证的服务(见TestLogin()方法中的"执行get请 ...
随机推荐
- jar包、war包
JavaSE程序可以打包成Jar包(J其实可以理解为Java了),而JavaWeb程序可以打包成war包(w其实可以理解为Web了).然后把war发布到Tomcat的webapps目录下,Tomcat ...
- golang -- 字符串就地取反
字符串 定义 在golang中字符串是一种不可变的字节序列,它可以包含任意的数据,包括0值字节,但主要是人类可以阅读的文本.golang中默认字符串被解读为utf-8编码的Unicode码点(文字符号 ...
- vue项目部署流程
用vue-cli搭建的做法1.npm run build2.把dist里的文件打包上传至服务器 例 /data/www/,我一般把index.html放在static里所以我的文件路径为:/data/ ...
- 第九次psp例行报告
本周psp 本周进度条 代码累积折线图 博文字数累积折线图 饼状图
- android随机运算器开发小结1
想到第一天自己写了一个简单的四则运算程序的情景:我便想起了引起我们不断迭代开发的程序背景是:二柱子接受老师安排的给孩子出题的任务,每次需要给孩子设置出题任务,生成相应的小学运算题目,所以我们面对的需求 ...
- Spring笔记⑤--整合hibernate代码测试
String整合hibernate代码测试 在上节生成的表中插入数据: 注意:使用myeclipse2014生成的整合项目可能存在问题需要我们自己导入. 第一步 我们写dao接口 packag ...
- mabatis报错 Result Maps collection already contains value for gamedataserver.dao.one.ChargeRecordMapper.BaseResultMap
1.解决这种报错看看英文"already ",也就是已经存在,其实是因为存在了两个id相同的返回,以下可以看看,根据这种例子看看自己项目是不是这种问题
- 封装react组件——三级联动
思路: 数据设计:省份为一维数组,一级市为二维数组,二级市/区/县为三维数组.这样设计的好处在于根据数组索引实现数据的关联. UI组件: MUI的DropDownMenu组件或Select Field ...
- selenium获取新页面标签页(只弹出一个新页面的切换)
selenium获取新页面标签页(只弹出一个新页面的切换) windows = driver.current_window_handle #定位当前页面句柄 all_handles = driver. ...
- Linux限制cpu睿频&限制频率
.关闭睿频 > /sys/devices/system/cpu/intel_pstate/no_turbo .限制CPU最大频率到50% " | sudo tee /sys/devic ...